diff options
author | Ryan Leckey <ryan@launchbadge.com> | 2017-06-16 11:15:40 -0700 |
---|---|---|
committer | Ryan Leckey <ryan@launchbadge.com> | 2017-06-16 11:15:40 -0700 |
commit | 05e9cd421e75173462072e57ab7e27881730d250 (patch) | |
tree | 8db6e8d6951c57a3d50093a8fd789c2941df849f | |
parent | 1716c2fe7571b03215a5ae09c91f508d03335c2a (diff) |
Support subscript access on path get/set
-rw-r--r-- | src/path/mod.rs | 78 | ||||
-rw-r--r-- | src/path/parser.rs | 2 | ||||
-rw-r--r-- | tests/Settings.toml | 6 | ||||
-rw-r--r-- | tests/get.rs | 11 | ||||
-rw-r--r-- | tests/set.rs | 16 |
5 files changed, 105 insertions, 8 deletions
diff --git a/src/path/mod.rs b/src/path/mod.rs index 4c4dc2b..046888e 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -10,7 +10,7 @@ mod parser; pub enum Expression { Identifier(String), Child(Box<Expression>, String), - Subscript(Box<Expression>, i32), + Subscript(Box<Expression>, isize), } impl FromStr for Expression { @@ -21,6 +21,14 @@ impl FromStr for Expression { } } +fn sindex_to_uindex(index: isize, len: usize) -> usize { + if index >= 0 { + index as usize + } else { + len - (index.abs() as usize) + } +} + impl Expression { pub fn get(self, root: &Value) -> Option<&Value> { match self { @@ -50,8 +58,26 @@ impl Expression { } } - _ => { - unimplemented!(); + Expression::Subscript(expr, index) => { + match expr.get(root) { + Some(value) => { + match value.kind { + ValueKind::Array(ref array) => { + let index = sindex_to_uindex(index, array.len()); + + if index >= array.len() { + None + } else { + Some(&array[index]) + } + } + + _ => None, + } + } + + _ => None, + } } } } @@ -92,8 +118,26 @@ impl Expression { } } - _ => { - unimplemented!(); + Expression::Subscript(ref expr, index) => { + match expr.get_mut(root) { + Some(value) => { + match value.kind { + ValueKind::Array(ref mut array) => { + let index = sindex_to_uindex(index, array.len()); + + if index >= array.len() { + array.resize((index + 1) as usize, Value::new(None, ValueKind::Nil)); + } + + Some(&mut array[index]) + } + + _ => None, + } + } + + _ => None, + } } } } @@ -151,8 +195,28 @@ impl Expression { } } - _ => { - unimplemented!(); + Expression::Subscript(ref expr, index) => { + if let Some(parent) = expr.get_mut(root) { + match parent.kind { + ValueKind::Array(ref mut array) => { + let uindex = sindex_to_uindex(index, array.len()); + + if uindex >= array.len() { + array.resize((uindex + 1) as usize, Value::new(None, ValueKind::Nil)); + } + + array[uindex] = value.clone(); + } + + _ => { + // Didn't find an array ... + // Add an array and do this again + *parent = Vec::<Value>::new().into(); + + Expression::Subscript(expr.clone(), index).set(parent, value); + } + } + } } } } diff --git a/src/path/parser.rs b/src/path/parser.rs index ee3fb65..d4b13dd 100644 --- a/src/path/parser.rs +++ b/src/path/parser.rs @@ -16,7 +16,7 @@ named!(ident_<String>, ) ); -named!(integer <i32>, +named!(integer <isize>, map_res!( map_res!( ws!(digit), diff --git a/tests/Settings.toml b/tests/Settings.toml index b7fc7e9..2f2da74 100644 --- a/tests/Settings.toml +++ b/tests/Settings.toml @@ -8,6 +8,12 @@ boolean_s_parse = "fals" arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +[[items]] +name = "1" + +[[items]] +name = "2" + [place] name = "Torre di Pisa" longitude = 43.7224985 diff --git a/tests/get.rs b/tests/get.rs index 2df50a5..1922fca 100644 --- a/tests/get.rs +++ b/tests/get.rs @@ -88,6 +88,17 @@ fn test_get_scalar_path() { } #[test] +fn test_get_scalar_path_subscript() { + let c = make(); + + assert_eq!(c.get("arr[2]").ok(), Some(3)); + assert_eq!(c.get("items[0].name").ok(), Some("1".to_string())); + assert_eq!(c.get("items[1].name").ok(), Some("2".to_string())); + assert_eq!(c.get("items[-1].name").ok(), Some("2".to_string())); + assert_eq!(c.get("items[-2].name").ok(), Some("1".to_string())); +} + +#[test] fn test_map() { let c = make(); let m: HashMap<String, Value> = c.get("place").unwrap(); diff --git a/tests/set.rs b/tests/set.rs index e02814b..14ddfae 100644 --- a/tests/set.rs +++ b/tests/set.rs @@ -40,6 +40,22 @@ fn test_set_scalar_path() { } #[test] +fn test_set_arr_path() { + let mut c = Config::default(); + + c.merge(File::new("tests/Settings", FileFormat::Toml)) + .unwrap(); + + c.set("items[0].name", "John").unwrap(); + + assert_eq!(c.get("items[0].name").ok(), Some("John".to_string())); + + c.set("items[2]", "George").unwrap(); + + assert_eq!(c.get("items[2]").ok(), Some("George".to_string())); +} + +#[test] fn test_set_capital() { let mut c = Config::default(); |