From dd828e613b821afb8b8a082f6f55352e987cb9bd Mon Sep 17 00:00:00 2001 From: Valerii Hiora Date: Wed, 25 Jun 2014 12:22:11 +0300 Subject: [PATCH 1/3] Path lookups --- src/toml.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/toml.rs b/src/toml.rs index 33be0f8..a4d6d03 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -34,6 +34,35 @@ //! //! [1]: https://github.com/mojombo/toml //! [2]: https://github.com/BurntSushi/toml-test +//! +//! # Lookups +//! +//! Sometimes it might be useful to decode/retrieve only internal +//! nodes. You can use `lookup` to get corresponding value. Note, that +//! it tries its best to traverse both tables and arrays. In the +//! latter case it expects a zero-based index as a path component +//! +//! ``` +//! use std::from_str::FromStr; +//! +//! let toml = r#" +//! [test] +//! foo = "bar" +//! +//! [[values]] +//! foo = "baz" +//! +//! [[values]] +//! foo = "qux" +//! "#; +//! let value: toml::Value = FromStr::from_str(toml).unwrap(); +//! let test_foo = value.lookup("test.foo").unwrap(); +//! println!("test_foo is {}", test_foo); +//! assert_eq!(test_foo.as_str().unwrap(), "bar"); +//! let foo1 = value.lookup("values.1.foo").unwrap(); +//! println!("foo1 is {}", foo1); +//! assert_eq!(foo1.as_str().unwrap(), "qux"); +//! ``` #![crate_type = "lib"] #![feature(macro_rules)] @@ -142,6 +171,45 @@ impl Value { pub fn as_table<'a>(&'a self) -> Option<&'a Table> { match *self { Table(ref s) => Some(s), _ => None } } + + /// Lookups for value at specified path. + /// + /// Uses '.' as a path separator. + /// + /// Note: arrays have zero-based indexes. + pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> { + Value::lookup_path(self, path.split('.')) + } + + // Performs actual traverse starting with value + // + // For arrays tries to convert key to uint and retrieve + // corresponding element + fn lookup_path<'a, I:Iterator<&'a str>>(value: &'a Value, + components: I) -> Option<&'a Value>{ + let mut cur_value: &'a Value = value; + let mut iter = components; + for key in iter { + match cur_value { + &Table(ref hm) => { + match hm.find_equiv::<'a>(&key) { + Some(v) => cur_value = v, + _ => return None + } + }, + &Array(ref v) => { + let idx: Option = FromStr::from_str(key); + match idx { + Some(idx) => cur_value = v.get::<'a>(idx), + _ => return None + } + }, + _ => return None + } + }; + + Some(cur_value) + } } impl FromStr for Value { From 2047fa3bc910ad3a2eec3f23309cd60064a63a7a Mon Sep 17 00:00:00 2001 From: Valerii Hiora Date: Wed, 25 Jun 2014 17:00:10 +0300 Subject: [PATCH 2/3] Added separate tests, moved comments --- src/toml.rs | 108 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 32 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index a4d6d03..3223c91 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -35,34 +35,6 @@ //! [1]: https://github.com/mojombo/toml //! [2]: https://github.com/BurntSushi/toml-test //! -//! # Lookups -//! -//! Sometimes it might be useful to decode/retrieve only internal -//! nodes. You can use `lookup` to get corresponding value. Note, that -//! it tries its best to traverse both tables and arrays. In the -//! latter case it expects a zero-based index as a path component -//! -//! ``` -//! use std::from_str::FromStr; -//! -//! let toml = r#" -//! [test] -//! foo = "bar" -//! -//! [[values]] -//! foo = "baz" -//! -//! [[values]] -//! foo = "qux" -//! "#; -//! let value: toml::Value = FromStr::from_str(toml).unwrap(); -//! let test_foo = value.lookup("test.foo").unwrap(); -//! println!("test_foo is {}", test_foo); -//! assert_eq!(test_foo.as_str().unwrap(), "bar"); -//! let foo1 = value.lookup("values.1.foo").unwrap(); -//! println!("foo1 is {}", foo1); -//! assert_eq!(foo1.as_str().unwrap(), "qux"); -//! ``` #![crate_type = "lib"] #![feature(macro_rules)] @@ -83,7 +55,6 @@ pub use serialization::{InvalidMapKeyLocation, InvalidMapKeyType}; mod parser; mod show; mod serialization; -#[cfg(test)] mod test; /// Representation of a TOML value. #[deriving(PartialEq, Clone)] @@ -177,6 +148,29 @@ impl Value { /// Uses '.' as a path separator. /// /// Note: arrays have zero-based indexes. + /// + /// ``` + /// let toml = r#" + /// [test] + /// foo = "bar" + /// + /// [[values]] + /// foo = "baz" + /// + /// [[values]] + /// foo = "qux" + /// "#; + /// let value: toml::Value = from_str(toml).unwrap(); + /// + /// let foo = value.lookup("test.foo").unwrap(); + /// assert_eq!(foo.as_str().unwrap(), "bar"); + /// + /// let foo = value.lookup("values.1.foo").unwrap(); + /// assert_eq!(foo.as_str().unwrap(), "qux"); + /// + /// let no_bar = value.lookup("test.bar"); + /// assert_eq!(no_bar.is_none(), true); + /// ``` pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> { Value::lookup_path(self, path.split('.')) } @@ -186,13 +180,13 @@ impl Value { // For arrays tries to convert key to uint and retrieve // corresponding element fn lookup_path<'a, I:Iterator<&'a str>>(value: &'a Value, - components: I) -> Option<&'a Value>{ + components: I) -> Option<&'a Value> { let mut cur_value: &'a Value = value; let mut iter = components; for key in iter { match cur_value { &Table(ref hm) => { - match hm.find_equiv::<'a>(&key) { + match hm.find_equiv(&key) { Some(v) => cur_value = v, _ => return None } @@ -200,7 +194,7 @@ impl Value { &Array(ref v) => { let idx: Option = FromStr::from_str(key); match idx { - Some(idx) => cur_value = v.get::<'a>(idx), + Some(idx) if idx < v.len() => cur_value = v.get(idx), _ => return None } }, @@ -217,3 +211,53 @@ impl FromStr for Value { Parser::new(s).parse().map(Table) } } + + +#[cfg(test)] +mod test { + use super::Value; + + #[test] + fn lookup_valid() { + let toml = r#" + [test] + foo = "bar" + + [[values]] + foo = "baz" + + [[values]] + foo = "qux" + "#; + + let value: Value = from_str(toml).unwrap(); + + let test_foo = value.lookup("test.foo").unwrap(); + assert_eq!(test_foo.as_str().unwrap(), "bar"); + + let foo1 = value.lookup("values.1.foo").unwrap(); + assert_eq!(foo1.as_str().unwrap(), "qux"); + + let no_bar = value.lookup("test.bar"); + assert!(no_bar.is_none()); + } + + #[test] + fn lookup_invalid_index() { + let toml = r#" + [[values]] + foo = "baz" + "#; + + let value: Value = from_str(toml).unwrap(); + + let foo = value.lookup("test.foo"); + assert!(foo.is_none()); + + let foo = value.lookup("values.100.foo"); + assert!(foo.is_none()); + + let foo = value.lookup("values.str.foo"); + assert!(foo.is_none()); + } +} From 13cc8a9201fd8ed5d80ea57a08104aa79a5e757e Mon Sep 17 00:00:00 2001 From: Valerii Hiora Date: Fri, 27 Jun 2014 17:28:55 +0300 Subject: [PATCH 3/3] Fixed tests, merged lookup functions --- src/toml.rs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index 3223c91..440da6a 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -55,7 +55,7 @@ pub use serialization::{InvalidMapKeyLocation, InvalidMapKeyType}; mod parser; mod show; mod serialization; - +#[cfg(test)]mod test; /// Representation of a TOML value. #[deriving(PartialEq, Clone)] #[allow(missing_doc)] @@ -172,18 +172,8 @@ impl Value { /// assert_eq!(no_bar.is_none(), true); /// ``` pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> { - Value::lookup_path(self, path.split('.')) - } - - // Performs actual traverse starting with value - // - // For arrays tries to convert key to uint and retrieve - // corresponding element - fn lookup_path<'a, I:Iterator<&'a str>>(value: &'a Value, - components: I) -> Option<&'a Value> { - let mut cur_value: &'a Value = value; - let mut iter = components; - for key in iter { + let mut cur_value = self; + for key in path.split('.') { match cur_value { &Table(ref hm) => { match hm.find_equiv(&key) { @@ -212,9 +202,8 @@ impl FromStr for Value { } } - #[cfg(test)] -mod test { +mod tests { use super::Value; #[test]