Skip to content

Commit

Permalink
build out initial key values API
Browse files Browse the repository at this point in the history
  • Loading branch information
KodrAus committed Apr 9, 2019
1 parent 75043f9 commit f21ce41
Show file tree
Hide file tree
Showing 11 changed files with 970 additions and 5 deletions.
13 changes: 10 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@ rust:
- stable
- beta
- nightly
install:
- '[ "$TRAVIS_RUST_VERSION" == "1.16.0" ] || rustup target add thumbv6m-none-eabi'
matrix:
include:
- rust: 1.21.0
script:
- cargo test --verbose --features kv_unstable
- cargo test --verbose --features "kv_unstable std"
- rust: 1.16.0
script:
- rustup target add thumbv6m-none-eabi
- cargo build --verbose --target=thumbv6m-none-eabi
script:
- cargo build --verbose
- cargo build --verbose --features serde
- cargo build --verbose --features std
- '[ "$TRAVIS_RUST_VERSION" == "1.16.0" ] || cargo build --verbose --target=thumbv6m-none-eabi'
- cargo test --verbose
- cargo test --verbose --features serde
- cargo test --verbose --features std
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ release_max_level_trace = []

std = []

# requires Rust `>= 1.21.0`
kv_unstable = []

[badges]
travis-ci = { repository = "rust-lang-nursery/log" }
appveyor = { repository = "alexcrichton/log" }
Expand Down
50 changes: 50 additions & 0 deletions src/key_values/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::fmt;

/// An error encountered while working with structured data.
#[derive(Clone, Debug)]
pub struct KeyValueError {
msg: &'static str,
}

impl KeyValueError {
/// Create an error from the given message.
pub fn msg(msg: &'static str) -> Self {
KeyValueError {
msg: msg,
}
}
}

impl fmt::Display for KeyValueError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.msg.fmt(f)
}
}

impl From<fmt::Error> for KeyValueError {
fn from(_: fmt::Error) -> Self {
KeyValueError::msg("formatting failed")
}
}

impl From<KeyValueError> for fmt::Error {
fn from(_: KeyValueError) -> Self {
fmt::Error
}
}

#[cfg(feature = "std")]
mod std_support {
use super::*;
use std::error;

impl error::Error for KeyValueError {
fn description(&self) -> &str {
self.msg
}

fn cause(&self) -> Option<&error::Error> {
None
}
}
}
143 changes: 143 additions & 0 deletions src/key_values/key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//! Structured keys.
use std::fmt;
use std::cmp;
use std::hash;
use std::borrow::Borrow;

/// A type that can be converted into a [`Key`](struct.Key.html).
pub trait ToKey {
/// Perform the covnersion.
fn to_key(&self) -> Key;
}

impl<'a, T> ToKey for &'a T
where
T: ToKey + ?Sized,
{
fn to_key(&self) -> Key {
(**self).to_key()
}
}

impl<'k> ToKey for Key<'k> {
fn to_key(&self) -> Key {
Key {
key: self.key,
}
}
}

impl<'k> ToKey for &'k str {
fn to_key(&self) -> Key {
Key::from_str(self)
}
}

/// A key in a structured key-value pair.
#[derive(Clone)]
pub struct Key<'k> {
key: &'k str,
}

impl<'k> Key<'k> {
/// Get a key from a borrowed string.
pub fn from_str(key: &'k str) -> Self {
Key {
key: key,
}
}

/// Get a borrowed string from this key.
pub fn as_str(&self) -> &str {
self.key
}
}

impl<'k> fmt::Debug for Key<'k> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.key.fmt(f)
}
}

impl<'k> fmt::Display for Key<'k> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.key.fmt(f)
}
}

impl<'k> hash::Hash for Key<'k> {
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
self.as_str().hash(state)
}
}

impl<'k, 'ko> PartialEq<Key<'ko>> for Key<'k> {
fn eq(&self, other: &Key<'ko>) -> bool {
self.as_str().eq(other.as_str())
}
}

impl<'k> Eq for Key<'k> {}

impl<'k, 'ko> PartialOrd<Key<'ko>> for Key<'k> {
fn partial_cmp(&self, other: &Key<'ko>) -> Option<cmp::Ordering> {
self.as_str().partial_cmp(other.as_str())
}
}

impl<'k> Ord for Key<'k> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}

impl<'k> AsRef<str> for Key<'k> {
fn as_ref(&self) -> &str {
self.as_str()
}
}

impl<'k> Borrow<str> for Key<'k> {
fn borrow(&self) -> &str {
self.as_str()
}
}

impl<'k> From<&'k str> for Key<'k> {
fn from(s: &'k str) -> Self {
Key::from_str(s)
}
}

#[cfg(feature = "std")]
mod std_support {
use super::*;

use std::borrow::Cow;

impl ToKey for String {
fn to_key(&self) -> Key {
Key::from_str(self)
}
}

impl<'a> ToKey for Cow<'a, str> {
fn to_key(&self) -> Key {
Key::from_str(self)
}
}
}

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

#[test]
fn key_from_string() {
assert_eq!("a key", Key::from_str("a key").as_str());
}
}
11 changes: 11 additions & 0 deletions src/key_values/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! Structured key-value pairs.
mod error;
pub mod source;
pub mod key;
pub mod value;

pub use self::error::KeyValueError;
pub use self::source::Source;
pub use self::key::Key;
pub use self::value::Value;
116 changes: 116 additions & 0 deletions src/key_values/source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//! Sources for key-value pairs.
pub use key_values::{KeyValueError, Key, Value};

use key_values::key::ToKey;
use key_values::value::ToValue;

/// A source of key-value pairs.
///
/// The source may be a single pair, a set of pairs, or a filter over a set of pairs.
/// Use the [`Visitor`](struct.Visitor.html) trait to inspect the structured data
/// in a source.
pub trait Source {
/// Visit key-value pairs.
///
/// A source doesn't have to guarantee any ordering or uniqueness of pairs.
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), KeyValueError>;
}

impl<'a, T> Source for &'a T
where
T: Source + ?Sized,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), KeyValueError> {
(**self).visit(visitor)
}
}

impl<K, V> Source for (K, V)
where
K: ToKey,
V: ToValue,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), KeyValueError> {
visitor.visit_pair(self.0.to_key(), self.1.to_value())
}
}

impl<S> Source for [S]
where
S: Source,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), KeyValueError> {
for source in self {
source.visit(visitor)?;
}

Ok(())
}
}

impl<S> Source for Option<S>
where
S: Source,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), KeyValueError> {
if let Some(ref source) = *self {
source.visit(visitor)?;
}

Ok(())
}
}

/// A visitor for the key-value pairs in a [`Source`](trait.Source.html).
pub trait Visitor<'kvs> {
/// Visit a key-value pair.
fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), KeyValueError>;
}

impl<'a, 'kvs, T> Visitor<'kvs> for &'a mut T
where
T: Visitor<'kvs> + ?Sized,
{
fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), KeyValueError> {
(**self).visit_pair(key, value)
}
}

#[cfg(feature = "std")]
mod std_support {
use super::*;

impl<S> Source for Box<S>
where
S: Source + ?Sized,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), KeyValueError> {
(**self).visit(visitor)
}
}

impl<S> Source for Vec<S>
where
S: Source,
{
fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), KeyValueError> {
(**self).visit(visitor)
}
}
}

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

#[test]
fn source_is_object_safe() {
fn _check(_: &Source) {}
}

#[test]
fn visitor_is_object_safe() {
fn _check(_: &Visitor) {}
}
}
Loading

0 comments on commit f21ce41

Please sign in to comment.