Merge pull request #89 from BourgondAries/master

Allow advanced lookups in lookup and lookup_mut
This commit is contained in:
Alex Crichton 2016-03-27 22:54:34 -07:00
commit c53fceb42c
2 changed files with 132 additions and 10 deletions

View file

@ -181,12 +181,16 @@ impl Value {
/// assert_eq!(no_bar.is_none(), true); /// assert_eq!(no_bar.is_none(), true);
/// ``` /// ```
pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> { 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; let mut cur_value = self;
if path.len() == 0 { if path.len() == 0 {
return Some(cur_value) return Some(cur_value)
} }
for key in path.split('.') { for key in path {
match *cur_value { match *cur_value {
Value::Table(ref hm) => { Value::Table(ref hm) => {
match hm.get(key) { match hm.get(key) {
@ -205,8 +209,8 @@ impl Value {
}; };
Some(cur_value) Some(cur_value)
}
}
/// Lookups for mutable value at specified path. /// Lookups for mutable value at specified path.
/// ///
/// Uses '.' as a path separator. /// Uses '.' as a path separator.
@ -237,12 +241,17 @@ impl Value {
/// assert_eq!(result.as_str().unwrap(), "foo"); /// assert_eq!(result.as_str().unwrap(), "foo");
/// ``` /// ```
pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> { 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; let mut cur = self;
if path.len() == 0 { if path.len() == 0 {
return Some(cur) return Some(cur)
} }
for key in path.split('.') { for key in path {
let tmp = cur; let tmp = cur;
match *tmp { match *tmp {
Value::Table(ref mut hm) => { Value::Table(ref mut hm) => {
@ -428,4 +437,56 @@ mod tests {
let baz = foo.lookup("foo").unwrap(); let baz = foo.lookup("foo").unwrap();
assert_eq!(baz.as_str().unwrap(), "bar"); 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());
}
} }

View file

@ -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` // Parse a single key name starting at `start`
fn key_name(&mut self) -> Option<String> { fn key_name(&mut self) -> Option<String> {
let start = self.next_pos(); let start = self.next_pos();
@ -969,6 +994,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] #[test]
fn crlf() { fn crlf() {
let mut p = Parser::new("\ let mut p = Parser::new("\
@ -1246,10 +1307,10 @@ trimmed in raw strings.
assert!(table.lookup("foo_3").is_some()); assert!(table.lookup("foo_3").is_some());
assert!(table.lookup("foo_-2--3--r23f--4-f2-4").is_some()); assert!(table.lookup("foo_-2--3--r23f--4-f2-4").is_some());
assert!(table.lookup("a").is_some()); assert!(table.lookup("a").is_some());
assert!(table.lookup("!").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("\"character encoding\"").is_some());
assert!(table.lookup("ʎǝʞ").is_some()); assert!(table.lookup("'ʎǝʞ'").is_some());
} }
#[test] #[test]
@ -1293,9 +1354,9 @@ trimmed in raw strings.
"); ");
let table = Table(p.parse().unwrap()); let table = Table(p.parse().unwrap());
assert!(table.lookup("a.b").is_some()); assert!(table.lookup("a.b").is_some());
assert!(table.lookup("f f").is_some()); assert!(table.lookup("\"f f\"").is_some());
assert!(table.lookup("\"").is_some()); assert!(table.lookup("\"\\\"\"").is_some());
assert!(table.lookup("\"\"").is_some()); assert!(table.lookup("'\"\"'").is_some());
} }
#[test] #[test]