Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Source::get method for finding a single value #335

Merged
merged 1 commit into from
Jul 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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