aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBourgond Aries <macocio@gmail.com>2016-03-27 20:46:19 +0200
committerBourgond Aries <macocio@gmail.com>2016-03-27 20:46:19 +0200
commit3517215eab1a2846b1059cf8182cb9d621a6623e (patch)
tree83424e0436d3c557176483ff3e16b5e14eb50665 /src
parentb171205c57f6b8cd7eb3a175946293ab5b41988f (diff)
downloadmilf-rs-3517215eab1a2846b1059cf8182cb9d621a6623e.tar.gz
milf-rs-3517215eab1a2846b1059cf8182cb9d621a6623e.zip
Implement a more advanced algorithm for lookup
The new algorithm allows the explicit usage of "" and '' to denote key names. This is useful for accessing tables or keys that are named in a non-conventional manner.
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs52
-rw-r--r--src/parser.rs90
2 files changed, 133 insertions, 9 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 8afaa3d..9019327 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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.
@@ -278,6 +282,7 @@ impl FromStr for Value {
#[cfg(test)]
mod tests {
use super::Value;
+ use parser::Parser;
#[test]
fn lookup_mut_change() {
@@ -428,4 +433,47 @@ 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_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("")]);
+ }
+
}
diff --git a/src/parser.rs b/src/parser.rs
index 185f325..35b57fa 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -290,6 +290,46 @@ impl<'a> Parser<'a> {
}
}
+ // Parse an array index as a natural number
+ fn array_index(&mut self) -> Option<String> {
+ let mut index = String::new();
+ while let Some((_, ch)) = self.peek(0) {
+ match ch {
+ v @ '0' ... '9' => {
+ if !self.eat(v) {
+ return None
+ }
+ index.push(v);
+ }
+ _ => return Some(index),
+ }
+ }
+ if index.len() > 0 {
+ return Some(index);
+ }
+ None
+ }
+
+ /// 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 +1010,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 +1322,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 +1369,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]