diff options
-rw-r--r-- | src/lib.rs | 67 | ||||
-rw-r--r-- | src/parser.rs | 75 |
2 files changed, 132 insertions, 10 deletions
@@ -181,12 +181,16 @@ impl Value { /// assert_eq!(no_bar.is_none(), true); /// ``` pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> { + let ref path = match Parser::new(path).lookup() { + Some(path) => path, + None => return None, + }; let mut cur_value = self; if path.len() == 0 { return Some(cur_value) } - for key in path.split('.') { + for key in path { match *cur_value { Value::Table(ref hm) => { match hm.get(key) { @@ -205,8 +209,8 @@ impl Value { }; Some(cur_value) - } + } /// Lookups for mutable value at specified path. /// /// Uses '.' as a path separator. @@ -237,12 +241,17 @@ impl Value { /// assert_eq!(result.as_str().unwrap(), "foo"); /// ``` pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> { + let ref path = match Parser::new(path).lookup() { + Some(path) => path, + None => return None, + }; + let mut cur = self; if path.len() == 0 { return Some(cur) } - for key in path.split('.') { + for key in path { let tmp = cur; match *tmp { Value::Table(ref mut hm) => { @@ -428,4 +437,56 @@ mod tests { let baz = foo.lookup("foo").unwrap(); assert_eq!(baz.as_str().unwrap(), "bar"); } + + #[test] + fn lookup_advanced() { + let value: Value = "[table]\n\"value\" = 0".parse().unwrap(); + let looked = value.lookup("table.\"value\"").unwrap(); + assert_eq!(*looked, Value::Integer(0)); + } + + #[test] + fn lookup_advanced_table() { + let value: Value = r#"[table."name.other"] value = "my value""#.parse().unwrap(); + let looked = value.lookup(r#"table."name.other".value"#).unwrap(); + assert_eq!(*looked, Value::String(String::from("my value"))); + } + + #[test] + fn lookup_mut_advanced() { + let mut value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); + let looked = value.lookup_mut("table.\"value\".1").unwrap(); + assert_eq!(*looked, Value::Integer(1)); + } + + #[test] + fn single_dot() { + let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(None, value.lookup(".")); + } + + #[test] + fn array_dot() { + let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(None, value.lookup("0.")); + } + + #[test] + fn dot_inside() { + let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(None, value.lookup("table.\"value.0\"")); + } + + #[test] + fn table_with_quotes() { + let value: Value = "[table.\"element\"]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(None, value.lookup("\"table.element\".\"value\".0")); + } + + #[test] + fn table_with_quotes_2() { + let value: Value = "[table.\"element\"]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(Value::Integer(0), *value.lookup("table.\"element\".\"value\".0").unwrap()); + } + } diff --git a/src/parser.rs b/src/parser.rs index 185f325..9eb506a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -290,6 +290,31 @@ impl<'a> Parser<'a> { } } + // Parse an array index as a natural number + fn array_index(&mut self) -> Option<String> { + self.integer(0, false, false) + } + + /// Parse a path into a vector of paths + pub fn lookup(&mut self) -> Option<Vec<String>> { + if self.input.len() == 0 { + return Some(vec![]); + } + let mut keys = Vec::new(); + loop { + self.ws(); + if let Some(s) = self.key_name() { + keys.push(s); + } else if let Some(s) = self.array_index() { + keys.push(s); + } else { + return None + } + self.ws(); + if !self.expect('.') { return Some(keys) } + } + } + // Parse a single key name starting at `start` fn key_name(&mut self) -> Option<String> { let start = self.next_pos(); @@ -970,6 +995,42 @@ mod tests { } #[test] + fn lookup_internal() { + let mut parser = Parser::new(r#"hello."world\t".a.0.'escaped'.value"#); + let result = vec![ + String::from("hello"), + String::from("world\t"), + String::from("a"), + String::from("0"), + String::from("escaped"), + String::from("value") + ]; + + assert_eq!(parser.lookup().unwrap(), result); + } + + #[test] + fn lookup_internal_void() { + let mut parser = Parser::new(""); + assert_eq!(parser.lookup().unwrap(), Vec::<String>::new()); + } + + #[test] + fn lookup_internal_simple() { + let mut parser = Parser::new("value"); + assert_eq!(parser.lookup().unwrap(), vec![String::from("value")]); + } + + // This is due to key_name not parsing an empty "" correctly. Disabled for now. + #[test] + #[ignore] + fn lookup_internal_quoted_void() { + let mut parser = Parser::new("\"\""); + assert_eq!(parser.lookup().unwrap(), vec![String::from("")]); + } + + + #[test] fn crlf() { let mut p = Parser::new("\ [project]\r\n\ @@ -1246,10 +1307,10 @@ trimmed in raw strings. assert!(table.lookup("foo_3").is_some()); assert!(table.lookup("foo_-2--3--r23f--4-f2-4").is_some()); assert!(table.lookup("a").is_some()); - assert!(table.lookup("!").is_some()); - assert!(table.lookup("\"").is_some()); - assert!(table.lookup("character encoding").is_some()); - assert!(table.lookup("ʎǝʞ").is_some()); + assert!(table.lookup("\"!\"").is_some()); + assert!(table.lookup("\"\\\"\"").is_some()); + assert!(table.lookup("\"character encoding\"").is_some()); + assert!(table.lookup("'ʎǝʞ'").is_some()); } #[test] @@ -1293,9 +1354,9 @@ trimmed in raw strings. "); let table = Table(p.parse().unwrap()); assert!(table.lookup("a.b").is_some()); - assert!(table.lookup("f f").is_some()); - assert!(table.lookup("\"").is_some()); - assert!(table.lookup("\"\"").is_some()); + assert!(table.lookup("\"f f\"").is_some()); + assert!(table.lookup("\"\\\"\"").is_some()); + assert!(table.lookup("'\"\"'").is_some()); } #[test] |