stringly_typed/
lib.rs

1//! A crate for updating values and indexing into Rust types at runtime.
2//!
3//! # Examples
4//!
5//! ```rust
6//! # extern crate stringly_typed;
7//! # #[macro_use]
8//! # extern crate stringly_typed_derive;
9//! use stringly_typed::{StringlyTyped, Value};
10//!
11//! #[derive(StringlyTyped)]
12//! struct Outer {
13//!   inner: Inner,
14//! }
15//!
16//! #[derive(StringlyTyped)]
17//! struct Inner {
18//!   x: f64,
19//!   y: i64,
20//! }
21//!
22//! # fn run() -> Result<(), ::stringly_typed::UpdateError> {
23//! let mut thing = Outer {
24//!   inner: Inner {
25//!     x: 3.14,
26//!     y: 42,
27//!   }
28//! };
29//!
30//! let key = "inner.y";
31//! let value = -7;
32//! thing.set_value(key.split("."), Value::from(value))?;
33//!
34//! let got = thing.get_value(key.split("."))?;
35//! assert_eq!(thing.inner.y, -7);
36//! # Ok(())
37//! # }
38//! # fn main() { run().unwrap() }
39//! ```
40
41#![cfg_attr(not(feature = "std"), no_std)]
42
43// create a "std"-like facade for when we're compiled as no_std
44#[cfg(not(feature = "std"))]
45mod std {
46    pub use core::iter;
47}
48
49// re-export the StringlyTyped custom derive
50#[allow(unused_imports)]
51#[macro_use]
52extern crate stringly_typed_derive;
53#[doc(hidden)]
54pub use stringly_typed_derive::*;
55
56pub const DOUBLE_TYPE: &'static str = "double";
57pub const INTEGER_TYPE: &'static str = "integer";
58pub const STRING_TYPE: &'static str = "string";
59
60/// The whole point.
61pub trait StringlyTyped {
62    fn get(&self, key: &str) -> Result<Value, UpdateError> {
63        self.get_value(key.split("."))
64    }
65
66    fn set(&mut self, key: &str, value: Value) -> Result<(), UpdateError> {
67        self.set_value(key.split("."), value)
68    }
69
70    fn set_value<K, S>(&mut self, keys: K, value: Value) -> Result<(), UpdateError>
71    where
72        K: IntoIterator<Item = S>,
73        S: AsRef<str>;
74
75    fn get_value<K, S>(&self, keys: K) -> Result<Value, UpdateError>
76    where
77        K: IntoIterator<Item = S>,
78        S: AsRef<str>;
79
80    fn data_type(&self) -> &'static str;
81}
82
83#[derive(Debug, Copy, Clone, PartialEq)]
84pub enum UpdateError {
85    TypeError {
86        found: &'static str,
87        expected: &'static str,
88    },
89    TooManyKeys {
90        elements_remaning: usize,
91    },
92    UnknownField {
93        valid_fields: &'static [&'static str],
94    },
95    CantSerialize { data_type: &'static str },
96}
97
98/// A dynamically typed value.
99#[derive(Debug, Clone, PartialEq)]
100pub enum Value {
101    Integer(i64),
102    Double(f64),
103    #[cfg(feature = "std")]
104    String(String),
105    #[doc(hidden)]
106    __NonExhaustive,
107}
108
109impl Value {
110    pub fn data_type(&self) -> &'static str {
111        match *self {
112            Value::Integer(_) => INTEGER_TYPE,
113            Value::Double(_) => DOUBLE_TYPE,
114            #[cfg(feature = "std")]
115            Value::String(_) => STRING_TYPE,
116            Value::__NonExhaustive => unreachable!(),
117        }
118    }
119}
120
121impl From<i64> for Value {
122    fn from(other: i64) -> Value {
123        Value::Integer(other)
124    }
125}
126
127impl From<f64> for Value {
128    fn from(other: f64) -> Value {
129        Value::Double(other)
130    }
131}
132
133#[cfg(feature = "std")]
134impl From<String> for Value {
135    fn from(other: String) -> Value {
136        Value::String(other)
137    }
138}
139
140#[cfg(feature = "std")]
141impl<'a> From<&'a str> for Value {
142    fn from(other: &'a str) -> Value {
143        Value::String(other.to_string())
144    }
145}
146
147macro_rules! impl_primitive_type {
148    ($(#[$attr:meta])* $type:ty, $variant:ident, $data_type:expr) => {
149        $(#[$attr])*
150        impl StringlyTyped for $type {
151            fn set_value<K, S>(&mut self, keys: K, value: Value) -> Result<(), UpdateError>
152            where K: IntoIterator<Item = S>,
153                  S: AsRef<str> 
154            {
155                let mut keys = keys.into_iter();
156                
157                if let Some(_) = keys.next() {
158                    let elements_remaning = keys.count() + 1;
159                    return Err(UpdateError::TooManyKeys { elements_remaning });
160                }
161
162                match value {
163                    Value::$variant(v) => {
164                        *self = v;
165                        Ok(())
166                    }
167                    _ => {
168                        let e = UpdateError::TypeError { 
169                            expected: self.data_type(), 
170                            found: value.data_type(),
171                        };
172                        Err(e)
173                    }
174                }
175            }
176
177            fn get_value<K, S>(&self, keys: K) -> Result<Value, UpdateError>
178            where K: IntoIterator<Item = S>,
179                S: AsRef<str>,
180            {
181                let mut keys = keys.into_iter();
182                
183                if let Some(_) = keys.next() {
184                    let elements_remaning = keys.count() + 1;
185                    return Err(UpdateError::TooManyKeys { elements_remaning });
186                }
187
188                Ok(self.clone().into())
189            }
190
191            fn data_type(&self) -> &'static str {
192                $data_type
193            }
194        }
195    };
196}
197
198impl_primitive_type!(i64, Integer, INTEGER_TYPE);
199impl_primitive_type!(f64, Double, DOUBLE_TYPE);
200impl_primitive_type!(#[cfg(feature = "std")] String, String, STRING_TYPE);
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use std::iter;
206
207    #[test]
208    fn update_some_primitives() {
209        let empty = iter::empty::<&str>();
210
211        let mut integer: i64 = 42;
212        integer
213            .set_value(empty.clone(), Value::Integer(-7))
214            .unwrap();
215        assert_eq!(integer, -7);
216
217        let mut float: f64 = 3.14;
218        float.set_value(empty.clone(), Value::Double(42.0)).unwrap();
219        assert_eq!(float, 42.0);
220    }
221
222    #[cfg(feature = "std")]
223    #[test]
224    fn update_a_string() {
225        let empty = iter::empty::<&str>();
226
227        let mut string = String::from("before");
228        let new_value = String::from("after");
229        string
230            .set_value(empty.clone(), new_value.clone().into())
231            .unwrap();
232        assert_eq!(string, new_value);
233    }
234
235    #[test]
236    fn get_some_primitives() {
237        let empty = iter::empty::<&str>();
238
239        let integer: i64 = 42;
240        let got = integer.get_value(empty.clone()).unwrap();
241        assert_eq!(got, Value::from(integer));
242
243        let float: f64 = 3.14;
244        let got = float.get_value(empty.clone()).unwrap();
245        assert_eq!(got, Value::from(float));
246    }
247
248    #[cfg(feature = "std")]
249    #[test]
250    fn get_a_string() {
251        let empty = iter::empty::<&str>();
252
253        let string = String::from("before");
254        let got = string.get_value(empty.clone()).unwrap();
255        assert_eq!(got, Value::from(string));
256    }
257
258    #[test]
259    fn primitives_detect_type_errors() {
260        let empty = iter::empty::<&str>();
261
262        let mut integer: i64 = 42;
263        let got = integer
264            .set_value(empty.clone(), Value::Double(0.0))
265            .unwrap_err();
266        assert_eq!(
267            got,
268            UpdateError::TypeError {
269                found: DOUBLE_TYPE,
270                expected: INTEGER_TYPE,
271            }
272        );
273
274        let mut float: f64 = 3.14;
275        let got = float
276            .set_value(empty.clone(), Value::Integer(0))
277            .unwrap_err();
278        assert_eq!(
279            got,
280            UpdateError::TypeError {
281                found: INTEGER_TYPE,
282                expected: DOUBLE_TYPE,
283            }
284        );
285    }
286
287    #[test]
288    fn primitives_detect_over_indexing() {
289        let key = "foo.bar".split(".");
290        let mut n: i64 = 42;
291        let should_be = UpdateError::TooManyKeys {
292            elements_remaning: 2,
293        };
294
295        let got = n.set_value(key, Value::Integer(7)).unwrap_err();
296        assert_eq!(got, should_be);
297    }
298}