blob: fc0915263aef9edd3f7d6d2f943516ea311fbf34 [file] [log] [blame]
/// The query resolver that operates on the AST and the TOML object
use toml::{map::Map, Value};
use crate::tokenizer::Token;
use crate::error::{Error, Result};
pub fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
// Cases:
//
// 1. Identifier, toml: table, ident present -> traverse
// 2. Identifier, toml: table, no indent present -> create Table
// 2.1 If next token -> traverse
// 2.2 no next token -> return created Table
// 3. Identifier, toml: array -> error
// 4. Index, toml: table -> error
// 5. Index, toml: array, idx present -> traverse
// 6. Index, toml: array, idx not present
// 6.1 -> next token is ident -> push Table
// 6.2 -> next token is index -> push Array
// then traverse
match *tokens {
Token::Identifier { ref ident, .. } => {
match toml {
&mut Value::Table(ref mut t) => {
if t.contains_key(ident) {
match tokens.next() {
Some(next) => resolve(t.get_mut(ident).unwrap(), next),
None => t.get_mut(ident).ok_or_else(|| unreachable!()),
}
} else {
match tokens.next() {
Some(next) => {
let subdoc = t.entry(ident.clone()).or_insert(Value::Table(Map::new()));
resolve(subdoc, next)
},
None => Ok(t.entry(ident.clone()).or_insert(Value::Table(Map::new()))),
}
}
},
&mut Value::Array(_) => Err(Error::NoIdentifierInArray(ident.clone())),
_ => unimplemented!()
}
}
Token::Index { idx , .. } => {
match toml {
&mut Value::Table(_) => Err(Error::NoIndexInTable(idx)),
&mut Value::Array(ref mut ary) => {
if ary.len() > idx {
match tokens.next() {
Some(next) => resolve(ary.get_mut(idx).unwrap(), next),
None => ary.get_mut(idx).ok_or_else(|| unreachable!()),
}
} else {
if let Some(next) = tokens.next() {
match **next {
Token::Identifier { .. } => {
ary.push(Value::Table(Map::new()));
},
Token::Index { .. } => {
ary.push(Value::Array(vec![]));
}
}
//resolve(toml, next)
panic!("Cannot do this")
} else {
unimplemented!()
}
}
}
_ => unimplemented!()
}
}
}
}
#[cfg(test)]
mod test {
use toml::from_str as toml_from_str;
use toml::Value;
use crate::tokenizer::*;
use super::resolve;
macro_rules! do_resolve {
( $toml:ident => $query:expr ) => {
resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
}
}
#[test]
fn test_resolve_empty_toml_simple_query() {
let mut toml = toml_from_str("").unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Table(_)));
match result {
&mut Value::Table(ref tab) => assert!(tab.is_empty()),
_ => assert!(false, "Expected Table, got something else"),
}
}
#[test]
fn test_resolve_present_bool() {
let mut toml = toml_from_str("example = true").unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Boolean(true)));
}
#[test]
fn test_resolve_present_integer() {
let mut toml = toml_from_str("example = 1").unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Integer(1)));
}
#[test]
fn test_resolve_present_float() {
let mut toml = toml_from_str("example = 1.0").unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Float(_)));
assert_eq!(result.as_float(), Some(1.0));
}
#[test]
fn test_resolve_present_string() {
let mut toml = toml_from_str("example = 'string'").unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::String(_)));
match result {
&mut Value::String(ref s) => assert_eq!("string", s),
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_present_array_bools() {
let mut toml = toml_from_str("example = [ true, false ]").unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Array(_)));
match result {
&mut Value::Array(ref ary) => {
assert_eq!(ary[0], Value::Boolean(true));
assert_eq!(ary[1], Value::Boolean(false));
},
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_present_array_integers() {
let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Array(_)));
match result {
&mut Value::Array(ref ary) => {
assert_eq!(ary[0], Value::Integer(1));
assert_eq!(ary[1], Value::Integer(1337));
},
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_present_array_floats() {
let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Array(_)));
match result {
&mut Value::Array(ref ary) => {
assert!(is_match!(ary[0], Value::Float(_)));
assert_eq!(ary[0].as_float(), Some(1.0));
assert!(is_match!(ary[1], Value::Float(_)));
assert_eq!(ary[1].as_float(), Some(133.25));
},
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_array_index_query_1() {
let mut toml = toml_from_str("example = [ 1 ]").unwrap();
let result = do_resolve!(toml => "example.[0]");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Integer(1)));
}
#[test]
fn test_resolve_array_index_query_2() {
let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
let result = do_resolve!(toml => "example.[4]");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Integer(5)));
}
#[test]
fn test_resolve_table_element_query() {
let mut toml = toml_from_str(r#"
[table]
value = 42
"#).unwrap();
let result = do_resolve!(toml => "table.value");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Integer(42)));
}
#[test]
fn test_resolve_table_with_many_elements_element_query() {
let mut toml = toml_from_str(r#"
[table]
value1 = 42
value2 = 43
value3 = 44
value4 = 45
value5 = 46
"#).unwrap();
let result = do_resolve!(toml => "table.value1");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Integer(42)));
}
#[test]
fn test_resolve_table_array_query() {
let mut toml = toml_from_str(r#"
[table]
value1 = [ 42.0, 50.0 ]
"#).unwrap();
let result = do_resolve!(toml => "table.value1");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Array(_)));
match result {
&mut Value::Array(ref ary) => {
assert!(is_match!(ary[0], Value::Float(_)));
assert_eq!(ary[0].as_float(), Some(42.0));
assert!(is_match!(ary[1], Value::Float(_)));
assert_eq!(ary[1].as_float(), Some(50.0));
},
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_table_array_element_query() {
let mut toml = toml_from_str(r#"
[table]
value1 = [ 42 ]
"#).unwrap();
let result = do_resolve!(toml => "table.value1.[0]");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Integer(42)));
}
#[test]
fn test_resolve_multi_table_query() {
let mut toml = toml_from_str(r#"
[table0]
value = [ 1 ]
[table1]
value = [ "Foo" ]
[table2]
value = [ 42.0 ]
[table3]
value = [ true ]
"#).unwrap();
let result = do_resolve!(toml => "table1.value.[0]");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::String(_)));
match result {
&mut Value::String(ref s) => assert_eq!("Foo", s),
_ => panic!("What just happened?"),
}
}
static FRUIT_TABLE : &'static str = r#"
[[fruit.blah]]
name = "apple"
[fruit.blah.physical]
color = "red"
shape = "round"
[[fruit.blah]]
name = "banana"
[fruit.blah.physical]
color = "yellow"
shape = "bent"
"#;
#[test]
fn test_resolve_array_table_query_1() {
let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
let result = do_resolve!(toml => "fruit.blah.[0].name");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::String(_)));
match result {
&mut Value::String(ref s) => assert_eq!("apple", s),
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_array_table_query_2() {
let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
let result = do_resolve!(toml => "fruit.blah.[0].physical");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Table(_)));
match result {
&mut Value::Table(ref tab) => {
match tab.get("color") {
Some(&Value::String(ref s)) => assert_eq!("red", s),
_ => assert!(false),
}
match tab.get("shape") {
Some(&Value::String(ref s)) => assert_eq!("round", s),
_ => assert!(false),
}
},
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_query_on_result() {
let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
let result = do_resolve!(toml => "fruit.blah.[1].physical");
assert!(result.is_ok());
let result = result.unwrap();
let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
let result = resolve(result, &tokens);
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::String(_)));
match result {
&mut Value::String(ref s) => assert_eq!("yellow", s),
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_query_empty_table() {
let mut toml = toml_from_str(r#"
[example]
"#).unwrap();
let result = do_resolve!(toml => "example");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Table(_)));
match result {
&mut Value::Table(ref t) => assert!(t.is_empty()),
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_query_member_of_empty_table() {
let mut toml = toml_from_str("").unwrap();
let result = do_resolve!(toml => "example.foo");
assert!(result.is_ok());
let result = result.unwrap();
assert!(is_match!(result, &mut Value::Table(_)));
match result {
&mut Value::Table(ref t) => assert!(t.is_empty()),
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_query_index_in_table() {
let mut toml = toml_from_str("").unwrap();
let result = do_resolve!(toml => "example.[0]");
// TODO: Array creating is not yet implemented properly
assert!(result.is_err());
//assert!(result.is_ok());
//let result = result.unwrap();
//assert!(is_match!(result, &mut Value::Array(_)));
//match result {
// &mut Value::Array(ref a) => assert!(a.is_empty()),
// _ => panic!("What just happened?"),
//}
}
#[test]
fn test_resolve_query_identifier_in_array() {
let mut toml = toml_from_str("").unwrap();
let result = do_resolve!(toml => "example.foo.bar");
assert!(result.is_ok());
let result = result.unwrap();
match result {
&mut Value::Table(ref t) => assert!(t.is_empty()),
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_query_value_as_table() {
let mut toml = toml_from_str("").unwrap();
let result = do_resolve!(toml => "example.foo.bar");
assert!(result.is_ok());
let result = result.unwrap();
match result {
&mut Value::Table(ref t) => assert!(t.is_empty()),
_ => panic!("What just happened?"),
}
}
#[test]
fn test_resolve_query_value_as_array() {
let mut toml = toml_from_str("").unwrap();
let result = do_resolve!(toml => "example.foo.[0]");
// TODO: Array creating is not yet implemented properly
assert!(result.is_err());
//assert!(result.is_ok());
//let result = result.unwrap();
//match result {
// &mut Value::Array(ref a) => assert!(a.is_empty()),
// _ => panic!("What just happened?"),
//}
}
}