diff --git a/src/kv/source.rs b/src/kv/source.rs index 6552ce815..4d7d1cefd 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -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> { + struct Get<'k, 'v> { + key: Key<'k>, + found: Option>, + } + + 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 @@ -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> { + Source::get(&**self, key) } fn count(&self) -> usize { - (**self).count() + Source::count(&**self) } } @@ -68,6 +106,14 @@ where visitor.visit_pair(self.0.to_key(), self.1.to_value()) } + fn get<'v>(&'v self, key: Key) -> Option> { + if self.0.to_key() == key { + Some(self.1.to_value()) + } else { + None + } + } + fn count(&self) -> usize { 1 } @@ -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> { + Source::get(&**self, key) } fn count(&self) -> usize { - (**self).count() + Source::count(&**self) } } @@ -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> { + Source::get(&**self, key) } fn count(&self) -> usize { - (**self).count() + Source::count(&**self) } } @@ -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() { @@ -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()); + } } diff --git a/src/kv/value/impls.rs b/src/kv/value/impls.rs index f4c01d72f..651097d27 100644 --- a/src/kv/value/impls.rs +++ b/src/kv/value/impls.rs @@ -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 `::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::.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::.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); - - impl Visitor for TestVisitor - 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::.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::.to_value().to_token(), Token::None); } } diff --git a/src/kv/value/mod.rs b/src/kv/value/mod.rs index ff0db6c63..ddde43314 100644 --- a/src/kv/value/mod.rs +++ b/src/kv/value/mod.rs @@ -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}; diff --git a/src/kv/value/test.rs b/src/kv/value/test.rs new file mode 100644 index 000000000..5f3296f6a --- /dev/null +++ b/src/kv/value/test.rs @@ -0,0 +1,156 @@ +// Test support for inspecting Values + +use std::fmt::{self, Write}; +use std::str; + +use super::{Value, Error}; +use super::internal::Visitor; + +#[derive(Debug, PartialEq)] +pub(in kv) enum Token { + U64(u64), + I64(i64), + F64(f64), + Char(char), + Bool(bool), + Str(StrBuf), + None, +} + +#[derive(Debug, PartialEq)] +pub(in kv) struct StrBuf { + buf: [u8; 16], + len: usize, +} + +impl<'a> From<&'a str> for StrBuf { + fn from(s: &'a str) -> Self { + let mut buf = Buffer::new(); + write!(&mut buf, "{}", s).unwrap(); + + buf.into_str_buf() + } +} + +impl PartialEq for StrBuf { + fn eq(&self, other: &str) -> bool { + self.as_ref() == other + } +} + +impl<'a> PartialEq<&'a str> for StrBuf { + fn eq(&self, other: &&'a str) -> bool { + self.as_ref() == *other + } +} + +impl AsRef for StrBuf { + fn as_ref(&self) -> &str { + str::from_utf8(&self.buf[0..self.len]).unwrap() + } +} + +#[cfg(test)] +impl<'v> Value<'v> { + pub(in kv) fn to_token(&self) -> Token { + struct TestVisitor(Option); + + impl Visitor for TestVisitor { + fn debug(&mut self, v: &fmt::Debug) -> Result<(), Error> { + let mut buf = Buffer::new(); + write!(&mut buf, "{:?}", v)?; + + self.0 = Some(Token::Str(buf.into_str_buf())); + Ok(()) + } + + fn u64(&mut self, v: u64) -> Result<(), Error> { + self.0 = Some(Token::U64(v)); + Ok(()) + } + + fn i64(&mut self, v: i64) -> Result<(), Error> { + self.0 = Some(Token::I64(v)); + Ok(()) + } + + fn f64(&mut self, v: f64) -> Result<(), Error> { + self.0 = Some(Token::F64(v)); + Ok(()) + } + + fn bool(&mut self, v: bool) -> Result<(), Error> { + self.0 = Some(Token::Bool(v)); + Ok(()) + } + + fn char(&mut self, v: char) -> Result<(), Error> { + self.0 = Some(Token::Char(v)); + Ok(()) + } + + fn str(&mut self, v: &str) -> Result<(), Error> { + self.0 = Some(Token::Str(v.into())); + Ok(()) + } + + fn none(&mut self) -> Result<(), Error> { + self.0 = Some(Token::None); + Ok(()) + } + } + + let mut visitor = TestVisitor(None); + self.visit(&mut visitor).unwrap(); + + visitor.0.unwrap() + } + + pub(in kv) fn to_str_buf(&self) -> StrBuf { + let mut buf = Buffer::new(); + write!(&mut buf, "{}", self).unwrap(); + + buf.into_str_buf() + } +} + +// 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 into_str_buf(self) -> StrBuf { + StrBuf { + buf: self.buf, + len: 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(()) + } +}