summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Leckey <ryan@launchbadge.com>2017-06-16 11:15:40 -0700
committerRyan Leckey <ryan@launchbadge.com>2017-06-16 11:15:40 -0700
commit05e9cd421e75173462072e57ab7e27881730d250 (patch)
tree8db6e8d6951c57a3d50093a8fd789c2941df849f
parent1716c2fe7571b03215a5ae09c91f508d03335c2a (diff)
Support subscript access on path get/set
-rw-r--r--src/path/mod.rs78
-rw-r--r--src/path/parser.rs2
-rw-r--r--tests/Settings.toml6
-rw-r--r--tests/get.rs11
-rw-r--r--tests/set.rs16
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();