From 9b1dc843fcd2158930cc8b9de1bfc9cb8eedf127 Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Thu, 24 Mar 2016 14:18:00 +0100 Subject: [PATCH 1/3] Add lookup_mut method for mutable access Mutable access may sometimes be desired in order to change values in the toml table. This can be used for dynamic configurations which will be easy to modify and store. lookup_mut requires a recursive method due to the borrow checker not allowing to have more than one mutable reference in the same scope. --- src/lib.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a5ecb5d..21cdf3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,7 @@ use std::collections::BTreeMap; use std::str::FromStr; +use std::str::Split; pub use parser::{Parser, ParserError}; @@ -206,6 +207,67 @@ impl Value { Some(cur_value) } + + fn lookup_mut_recurse<'a>(&'a mut self, matches: &mut Split<'a, char>) -> Option<&'a mut Value> { + if let Some(key) = matches.next() { + match *self { + Value::Table(ref mut hm) => { + match hm.get_mut(key) { + Some(v) => return v.lookup_mut_recurse(matches), + None => return None, + } + }, + Value::Array(ref mut v) => { + match key.parse::().ok() { + Some(idx) if idx < v.len() + => return (&mut v[idx]).lookup_mut_recurse(matches), + _ => return None, + } + }, + _ => return None + } + } + Some(self) + } + + /// Lookups for mutable value at specified path. + /// + /// Uses '.' as a path separator. + /// + /// Note: arrays have zero-based indexes. + /// + /// Note: empty path returns self. + /// + /// ``` + /// # #![allow(unstable)] + /// let toml = r#" + /// [test] + /// foo = "bar" + /// + /// [[values]] + /// foo = "baz" + /// + /// [[values]] + /// foo = "qux" + /// "#; + /// let mut value: toml::Value = toml.parse().unwrap(); + /// { + /// let string = value.lookup_mut("test.foo").unwrap(); + /// assert_eq!(string, &mut toml::Value::String(String::from("bar"))); + /// *string = toml::Value::String(String::from("foo")); + /// } + /// let result = value.lookup_mut("test.foo").unwrap(); + /// assert_eq!(result.as_str().unwrap(), "foo"); + /// ``` + pub fn lookup_mut<'a>(&'a mut self, path: &'a str) -> Option<&'a mut Value> { + if path.len() == 0 { + return Some(self) + } + + let mut matches = path.split('.'); + self.lookup_mut_recurse(&mut matches) + } + } impl FromStr for Value { From 32969ca891a3021587863f0ec0b89ed7151d23b2 Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Fri, 25 Mar 2016 17:33:46 +0100 Subject: [PATCH 2/3] Implement the non-recursive lookup_mut Also include some tests simply by copying and modifying the other tests for lookup. --- src/lib.rs | 126 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 31 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 21cdf3e..d81b9cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,6 @@ use std::collections::BTreeMap; use std::str::FromStr; -use std::str::Split; pub use parser::{Parser, ParserError}; @@ -208,28 +207,6 @@ impl Value { Some(cur_value) } - fn lookup_mut_recurse<'a>(&'a mut self, matches: &mut Split<'a, char>) -> Option<&'a mut Value> { - if let Some(key) = matches.next() { - match *self { - Value::Table(ref mut hm) => { - match hm.get_mut(key) { - Some(v) => return v.lookup_mut_recurse(matches), - None => return None, - } - }, - Value::Array(ref mut v) => { - match key.parse::().ok() { - Some(idx) if idx < v.len() - => return (&mut v[idx]).lookup_mut_recurse(matches), - _ => return None, - } - }, - _ => return None - } - } - Some(self) - } - /// Lookups for mutable value at specified path. /// /// Uses '.' as a path separator. @@ -259,15 +236,32 @@ impl Value { /// let result = value.lookup_mut("test.foo").unwrap(); /// assert_eq!(result.as_str().unwrap(), "foo"); /// ``` - pub fn lookup_mut<'a>(&'a mut self, path: &'a str) -> Option<&'a mut Value> { - if path.len() == 0 { - return Some(self) - } - - let mut matches = path.split('.'); - self.lookup_mut_recurse(&mut matches) - } + pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> { + let mut cur = self; + if path.len() == 0 { + return Some(cur) + } + for key in path.split('.') { + let tmp = cur; + match *tmp { + Value::Table(ref mut hm) => { + match hm.get_mut(key) { + Some(v) => cur = v, + None => return None + } + } + Value::Array(ref mut v) => { + match key.parse::().ok() { + Some(idx) if idx < v.len() => cur = &mut v[idx], + _ => return None + } + } + _ => return None + } + } + Some(cur) + } } impl FromStr for Value { @@ -285,6 +279,76 @@ impl FromStr for Value { mod tests { use super::Value; + #[test] + fn lookup_mut_valid() { + let toml = r#" + [test] + foo = "bar" + + [[values]] + foo = "baz" + + [[values]] + foo = "qux" + "#; + + let mut value: Value = toml.parse().unwrap(); + + { + let test_foo = value.lookup_mut("test.foo").unwrap(); + assert_eq!(test_foo.as_str().unwrap(), "bar"); + } + + { + let foo1 = value.lookup_mut("values.1.foo").unwrap(); + assert_eq!(foo1.as_str().unwrap(), "qux"); + } + + assert!(value.lookup_mut("test.bar").is_none()); + assert!(value.lookup_mut("test.foo.bar").is_none()); + } + + #[test] + fn lookup_mut_invalid_index() { + let toml = r#" + [[values]] + foo = "baz" + "#; + + let mut value: Value = toml.parse().unwrap(); + + { + let foo = value.lookup_mut("test.foo"); + assert!(foo.is_none()); + } + + { + let foo = value.lookup_mut("values.100.foo"); + assert!(foo.is_none()); + } + + { + let foo = value.lookup_mut("values.str.foo"); + assert!(foo.is_none()); + } + } + + #[test] + fn lookup_mut_self() { + let mut value: Value = r#"foo = "bar""#.parse().unwrap(); + + { + let foo = value.lookup_mut("foo").unwrap(); + assert_eq!(foo.as_str().unwrap(), "bar"); + } + + let foo = value.lookup_mut("").unwrap(); + assert!(foo.as_table().is_some()); + + let baz = foo.lookup_mut("foo").unwrap(); + assert_eq!(baz.as_str().unwrap(), "bar"); + } + #[test] fn lookup_valid() { let toml = r#" From b171205c57f6b8cd7eb3a175946293ab5b41988f Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Fri, 25 Mar 2016 17:43:32 +0100 Subject: [PATCH 3/3] Add a test to see if the table's contents have changed --- src/lib.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d81b9cd..8afaa3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,6 +279,28 @@ impl FromStr for Value { mod tests { use super::Value; + #[test] + fn lookup_mut_change() { + let toml = r#" + [test] + foo = "bar" + + [[values]] + foo = "baz" + + [[values]] + foo = "qux" + "#; + + let mut value: Value = toml.parse().unwrap(); + { + let foo = value.lookup_mut("values.0.foo").unwrap(); + *foo = Value::String(String::from("bar")); + } + let foo = value.lookup("values.0.foo").unwrap(); + assert_eq!(foo.as_str().unwrap(), "bar"); + } + #[test] fn lookup_mut_valid() { let toml = r#"