Skip to content

Commit

Permalink
Merge pull request #335 from KodrAus/feat/source-get
Browse files Browse the repository at this point in the history
Add a Source::get method for finding a single value
  • Loading branch information
KodrAus committed Jul 1, 2019
2 parents 8dbb2f5 + 11ba139 commit 569175e
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 145 deletions.
88 changes: 82 additions & 6 deletions src/kv/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,40 @@ pub trait Source {
/// that visitor itself fails.
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error>;

/// Get the value for a given key.
///
/// If the key appears multiple times in the source then which key is returned
/// is implementation specific.
///
/// # Implementation notes
///
/// A source that can provide a more efficient implementation of this method
/// should override it.
fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
struct Get<'k, 'v> {
key: Key<'k>,
found: Option<Value<'v>>,
}

impl<'k, 'kvs> Visitor<'kvs> for Get<'k, 'kvs> {
fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> {
if self.key == key {
self.found = Some(value);
}

Ok(())
}
}

let mut get = Get {
key,
found: None,
};

let _ = self.visit(&mut get);
get.found
}

/// Count the number of key-value pairs that can be visited.
///
/// # Implementation notes
Expand Down Expand Up @@ -51,11 +85,15 @@ where
T: Source + ?Sized,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> {
(**self).visit(visitor)
Source::visit(&**self, visitor)
}

fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
Source::get(&**self, key)
}

fn count(&self) -> usize {
(**self).count()
Source::count(&**self)
}
}

Expand All @@ -68,6 +106,14 @@ where
visitor.visit_pair(self.0.to_key(), self.1.to_value())
}

fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
if self.0.to_key() == key {
Some(self.1.to_value())
} else {
None
}
}

fn count(&self) -> usize {
1
}
Expand Down Expand Up @@ -131,11 +177,15 @@ mod std_support {
S: Source + ?Sized,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> {
(**self).visit(visitor)
Source::visit(&**self, visitor)
}

fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
Source::get(&**self, key)
}

fn count(&self) -> usize {
(**self).count()
Source::count(&**self)
}
}

Expand All @@ -144,11 +194,15 @@ mod std_support {
S: Source,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> {
(**self).visit(visitor)
Source::visit(&**self, visitor)
}

fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
Source::get(&**self, key)
}

fn count(&self) -> usize {
(**self).count()
Source::count(&**self)
}
}

Expand All @@ -164,18 +218,29 @@ mod std_support {
#[cfg(test)]
mod tests {
use super::*;
use kv::value::test::Token;

#[test]
fn count() {
assert_eq!(1, Source::count(&Box::new(("a", 1))));
assert_eq!(2, Source::count(&vec![("a", 1), ("b", 2)]));
}

#[test]
fn get() {
let source = vec![("a", 1), ("b", 2), ("a", 1)];
assert_eq!(Token::I64(1), Source::get(&source, Key::from_str("a")).unwrap().to_token());

let source = Box::new(Option::None::<(&str, i32)>);
assert!(Source::get(&source, Key::from_str("a")).is_none());
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use kv::value::test::Token;

#[test]
fn source_is_object_safe() {
Expand Down Expand Up @@ -205,4 +270,15 @@ mod tests {
assert_eq!(0, Source::count(&Option::None::<(&str, i32)>));
assert_eq!(1, Source::count(&OnePair { key: "a", value: 1 }));
}

#[test]
fn get() {
let source = &[("a", 1), ("b", 2), ("a", 1)] as &[_];
assert_eq!(Token::I64(1), Source::get(source, Key::from_str("a")).unwrap().to_token());
assert_eq!(Token::I64(2), Source::get(source, Key::from_str("b")).unwrap().to_token());
assert!(Source::get(&source, Key::from_str("c")).is_none());

let source = Option::None::<(&str, i32)>;
assert!(Source::get(&source, Key::from_str("a")).is_none());
}
}
160 changes: 21 additions & 139 deletions src/kv/value/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,151 +264,33 @@ mod std_support {
#[cfg(test)]
mod tests {
use super::*;
use kv::value::Error;
use kv::value::internal::Visitor;

use std::fmt::Write;
use std::str::{self, Utf8Error};

// A quick-and-dirty no-std buffer
// to write strings into
struct Buffer {
buf: [u8; 16],
len: usize,
}

impl Buffer {
fn new() -> Self {
Buffer {
buf: [0; 16],
len: 0,
}
}

fn as_str(&self) -> Result<&str, Utf8Error> {
str::from_utf8(&self.buf[0..self.len])
}
}

impl Write for Buffer {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();

let end = self.len + bytes.len();

if end > 16 {
panic!("`{}` would overflow", s);
}

let buf = &mut self.buf[self.len..end];
buf.copy_from_slice(bytes);
self.len = end;

Ok(())
}
}
use kv::value::test::Token;

#[test]
fn test_to_value_display() {
// Write a value into our buffer using `<Value as Display>::fmt`
fn check(value: Value, expected: &str) {
let mut buf = Buffer::new();
write!(&mut buf, "{}", value).unwrap();

assert_eq!(expected, buf.as_str().unwrap());
}

check(42u64.to_value(), "42");
check(42i64.to_value(), "42");
check(42.01f64.to_value(), "42.01");
check(true.to_value(), "true");
check('a'.to_value(), "'a'");
check(format_args!("a {}", "value").to_value(), "a value");
check("a loong string".to_value(), "\"a loong string\"");
check(Some(true).to_value(), "true");
check(().to_value(), "None");
check(Option::None::<bool>.to_value(), "None");
assert_eq!(42u64.to_value().to_str_buf(), "42");
assert_eq!(42i64.to_value().to_str_buf(), "42");
assert_eq!(42.01f64.to_value().to_str_buf(), "42.01");
assert_eq!(true.to_value().to_str_buf(), "true");
assert_eq!('a'.to_value().to_str_buf(), "'a'");
assert_eq!(format_args!("a {}", "value").to_value().to_str_buf(), "a value");
assert_eq!("a loong string".to_value().to_str_buf(), "\"a loong string\"");
assert_eq!(Some(true).to_value().to_str_buf(), "true");
assert_eq!(().to_value().to_str_buf(), "None");
assert_eq!(Option::None::<bool>.to_value().to_str_buf(), "None");
}

#[test]
fn test_to_value_structured() {
#[derive(Debug, PartialEq)]
enum Token<'a> {
U64(u64),
I64(i64),
F64(f64),
Char(char),
Bool(bool),
Str(&'a str),
None,
}

struct TestVisitor<F>(F);

impl<F> Visitor for TestVisitor<F>
where
F: Fn(Token),
{
fn debug(&mut self, v: &fmt::Debug) -> Result<(), Error> {
let mut buf = Buffer::new();
write!(&mut buf, "{:?}", v)?;

let s = buf.as_str().map_err(|_| Error::msg("invalid UTF8"))?;
(self.0)(Token::Str(s));
Ok(())
}

fn u64(&mut self, v: u64) -> Result<(), Error> {
(self.0)(Token::U64(v));
Ok(())
}

fn i64(&mut self, v: i64) -> Result<(), Error> {
(self.0)(Token::I64(v));
Ok(())
}

fn f64(&mut self, v: f64) -> Result<(), Error> {
(self.0)(Token::F64(v));
Ok(())
}

fn bool(&mut self, v: bool) -> Result<(), Error> {
(self.0)(Token::Bool(v));
Ok(())
}

fn char(&mut self, v: char) -> Result<(), Error> {
(self.0)(Token::Char(v));
Ok(())
}

fn str(&mut self, v: &str) -> Result<(), Error> {
(self.0)(Token::Str(v));
Ok(())
}

fn none(&mut self) -> Result<(), Error> {
(self.0)(Token::None);
Ok(())
}
}

// Check that a value retains the right structure
fn check(value: Value, expected: Token) {
let mut visitor = TestVisitor(|token: Token| assert_eq!(expected, token));
value.visit(&mut visitor).unwrap();
}

check(42u64.to_value(), Token::U64(42));
check(42i64.to_value(), Token::I64(42));
check(42.01f64.to_value(), Token::F64(42.01));
check(true.to_value(), Token::Bool(true));
check('a'.to_value(), Token::Char('a'));
check(format_args!("a {}", "value").to_value(), Token::Str("a value"));
check("a loong string".to_value(), Token::Str("a loong string"));
check(Some(true).to_value(), Token::Bool(true));
check(().to_value(), Token::None);
check(Option::None::<bool>.to_value(), Token::None);
assert_eq!(42u64.to_value().to_token(), Token::U64(42));
assert_eq!(42i64.to_value().to_token(), Token::I64(42));
assert_eq!(42.01f64.to_value().to_token(), Token::F64(42.01));
assert_eq!(true.to_value().to_token(), Token::Bool(true));
assert_eq!('a'.to_value().to_token(), Token::Char('a'));
assert_eq!(format_args!("a {}", "value").to_value().to_token(), Token::Str("a value".into()));
assert_eq!("a loong string".to_value().to_token(), Token::Str("a loong string".into()));
assert_eq!(Some(true).to_value().to_token(), Token::Bool(true));
assert_eq!(().to_value().to_token(), Token::None);
assert_eq!(Option::None::<bool>.to_value().to_token(), Token::None);
}
}
3 changes: 3 additions & 0 deletions src/kv/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use std::fmt;
mod internal;
mod impls;

#[cfg(test)]
pub(in kv) mod test;

use kv::Error;

use self::internal::{Inner, Visit, Visitor};
Expand Down
Loading

0 comments on commit 569175e

Please sign in to comment.