toml_query/resolver/
mut_creating_resolver.rs

1use crate::error::{Error, Result};
2use crate::tokenizer::Token;
3/// The query resolver that operates on the AST and the TOML object
4use toml::{map::Map, Value};
5
6pub fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
7    // Cases:
8    //
9    //  1. Identifier, toml: table, ident present       -> traverse
10    //  2. Identifier, toml: table, no indent present   -> create Table
11    //      2.1 If next token                           -> traverse
12    //      2.2 no next token                           -> return created Table
13    //  3. Identifier, toml: array                      -> error
14    //  4. Index, toml: table                           -> error
15    //  5. Index, toml: array, idx present              -> traverse
16    //  6. Index, toml: array, idx not present
17    //      6.1 -> next token is ident                  -> push Table
18    //      6.2 -> next token is index                  -> push Array
19    //      then traverse
20
21    match *tokens {
22        Token::Identifier { ref ident, .. } => match toml {
23            Value::Table(ref mut t) => {
24                if t.contains_key(ident) {
25                    match tokens.next() {
26                        Some(next) => resolve(t.get_mut(ident).unwrap(), next),
27                        None => t.get_mut(ident).ok_or_else(|| unreachable!()),
28                    }
29                } else {
30                    match tokens.next() {
31                        Some(next) => {
32                            let subdoc = t.entry(ident.clone()).or_insert(Value::Table(Map::new()));
33                            resolve(subdoc, next)
34                        }
35                        None => Ok(t.entry(ident.clone()).or_insert(Value::Table(Map::new()))),
36                    }
37                }
38            }
39            Value::Array(_) => Err(Error::NoIdentifierInArray(ident.clone())),
40            _ => unimplemented!(),
41        },
42        Token::Index { idx, .. } => {
43            match toml {
44                Value::Table(_) => Err(Error::NoIndexInTable(idx)),
45                Value::Array(ref mut ary) => {
46                    if ary.len() > idx {
47                        match tokens.next() {
48                            Some(next) => resolve(ary.get_mut(idx).unwrap(), next),
49                            None => ary.get_mut(idx).ok_or_else(|| unreachable!()),
50                        }
51                    } else if let Some(next) = tokens.next() {
52                        match next {
53                            Token::Identifier { .. } => {
54                                ary.push(Value::Table(Map::new()));
55                            }
56                            Token::Index { .. } => {
57                                ary.push(Value::Array(vec![]));
58                            }
59                        }
60                        //resolve(toml, next)
61                        panic!("Cannot do this")
62                    } else {
63                        unimplemented!()
64                    }
65                }
66                _ => unimplemented!(),
67            }
68        }
69    }
70}
71
72#[cfg(test)]
73mod test {
74    use super::resolve;
75    use crate::tokenizer::*;
76    use toml::from_str as toml_from_str;
77    use toml::Value;
78
79    macro_rules! do_resolve {
80        ( $toml:ident => $query:expr ) => {
81            resolve(
82                &mut $toml,
83                &tokenize_with_seperator(&String::from($query), '.').unwrap(),
84            )
85        };
86    }
87
88    #[test]
89    fn test_resolve_empty_toml_simple_query() {
90        let mut toml = toml_from_str("").unwrap();
91        let result = do_resolve!(toml => "example");
92
93        assert!(result.is_ok());
94        let result = result.unwrap();
95        assert!(is_match!(result, Value::Table(_)));
96        match result {
97            Value::Table(ref tab) => assert!(tab.is_empty()),
98            _ => unreachable!("Expected Table, got something else"),
99        }
100    }
101
102    #[test]
103    fn test_resolve_present_bool() {
104        let mut toml = toml_from_str("example = true").unwrap();
105        let result = do_resolve!(toml => "example");
106
107        assert!(result.is_ok());
108        let result = result.unwrap();
109
110        assert!(is_match!(result, Value::Boolean(true)));
111    }
112
113    #[test]
114    fn test_resolve_present_integer() {
115        let mut toml = toml_from_str("example = 1").unwrap();
116        let result = do_resolve!(toml => "example");
117
118        assert!(result.is_ok());
119        let result = result.unwrap();
120
121        assert!(is_match!(result, Value::Integer(1)));
122    }
123
124    #[test]
125    fn test_resolve_present_float() {
126        let mut toml = toml_from_str("example = 1.0").unwrap();
127        let result = do_resolve!(toml => "example");
128
129        assert!(result.is_ok());
130        let result = result.unwrap();
131
132        assert!(is_match!(result, Value::Float(_)));
133        assert_eq!(result.as_float(), Some(1.0));
134    }
135
136    #[test]
137    fn test_resolve_present_string() {
138        let mut toml = toml_from_str("example = 'string'").unwrap();
139        let result = do_resolve!(toml => "example");
140
141        assert!(result.is_ok());
142        let result = result.unwrap();
143
144        assert!(is_match!(result, Value::String(_)));
145        match result {
146            Value::String(ref s) => assert_eq!("string", s),
147            _ => panic!("What just happened?"),
148        }
149    }
150
151    #[test]
152    fn test_resolve_present_array_bools() {
153        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
154        let result = do_resolve!(toml => "example");
155
156        assert!(result.is_ok());
157        let result = result.unwrap();
158
159        assert!(is_match!(result, Value::Array(_)));
160        match result {
161            Value::Array(ref ary) => {
162                assert_eq!(ary[0], Value::Boolean(true));
163                assert_eq!(ary[1], Value::Boolean(false));
164            }
165            _ => panic!("What just happened?"),
166        }
167    }
168
169    #[test]
170    fn test_resolve_present_array_integers() {
171        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
172        let result = do_resolve!(toml => "example");
173
174        assert!(result.is_ok());
175        let result = result.unwrap();
176
177        assert!(is_match!(result, Value::Array(_)));
178        match result {
179            Value::Array(ref ary) => {
180                assert_eq!(ary[0], Value::Integer(1));
181                assert_eq!(ary[1], Value::Integer(1337));
182            }
183            _ => panic!("What just happened?"),
184        }
185    }
186
187    #[test]
188    fn test_resolve_present_array_floats() {
189        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
190        let result = do_resolve!(toml => "example");
191
192        assert!(result.is_ok());
193        let result = result.unwrap();
194
195        assert!(is_match!(result, Value::Array(_)));
196        match result {
197            Value::Array(ref ary) => {
198                assert!(is_match!(ary[0], Value::Float(_)));
199                assert_eq!(ary[0].as_float(), Some(1.0));
200                assert!(is_match!(ary[1], Value::Float(_)));
201                assert_eq!(ary[1].as_float(), Some(133.25));
202            }
203            _ => panic!("What just happened?"),
204        }
205    }
206
207    #[test]
208    fn test_resolve_array_index_query_1() {
209        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
210        let result = do_resolve!(toml => "example.[0]");
211
212        assert!(result.is_ok());
213        let result = result.unwrap();
214
215        assert!(is_match!(result, Value::Integer(1)));
216    }
217
218    #[test]
219    fn test_resolve_array_index_query_2() {
220        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
221        let result = do_resolve!(toml => "example.[4]");
222
223        assert!(result.is_ok());
224        let result = result.unwrap();
225
226        assert!(is_match!(result, Value::Integer(5)));
227    }
228
229    #[test]
230    fn test_resolve_table_element_query() {
231        let mut toml = toml_from_str(
232            r#"
233        [table]
234        value = 42
235        "#,
236        )
237        .unwrap();
238        let result = do_resolve!(toml => "table.value");
239
240        assert!(result.is_ok());
241        let result = result.unwrap();
242
243        assert!(is_match!(result, Value::Integer(42)));
244    }
245
246    #[test]
247    fn test_resolve_table_with_many_elements_element_query() {
248        let mut toml = toml_from_str(
249            r#"
250        [table]
251        value1 = 42
252        value2 = 43
253        value3 = 44
254        value4 = 45
255        value5 = 46
256        "#,
257        )
258        .unwrap();
259        let result = do_resolve!(toml => "table.value1");
260
261        assert!(result.is_ok());
262        let result = result.unwrap();
263
264        assert!(is_match!(result, Value::Integer(42)));
265    }
266
267    #[test]
268    fn test_resolve_table_array_query() {
269        let mut toml = toml_from_str(
270            r#"
271        [table]
272        value1 = [ 42.0, 50.0 ]
273        "#,
274        )
275        .unwrap();
276        let result = do_resolve!(toml => "table.value1");
277
278        assert!(result.is_ok());
279        let result = result.unwrap();
280
281        assert!(is_match!(result, Value::Array(_)));
282        match result {
283            Value::Array(ref ary) => {
284                assert!(is_match!(ary[0], Value::Float(_)));
285                assert_eq!(ary[0].as_float(), Some(42.0));
286                assert!(is_match!(ary[1], Value::Float(_)));
287                assert_eq!(ary[1].as_float(), Some(50.0));
288            }
289            _ => panic!("What just happened?"),
290        }
291    }
292
293    #[test]
294    fn test_resolve_table_array_element_query() {
295        let mut toml = toml_from_str(
296            r#"
297        [table]
298        value1 = [ 42 ]
299        "#,
300        )
301        .unwrap();
302        let result = do_resolve!(toml => "table.value1.[0]");
303
304        assert!(result.is_ok());
305        let result = result.unwrap();
306
307        assert!(is_match!(result, Value::Integer(42)));
308    }
309
310    #[test]
311    fn test_resolve_multi_table_query() {
312        let mut toml = toml_from_str(
313            r#"
314        [table0]
315        value = [ 1 ]
316        [table1]
317        value = [ "Foo" ]
318        [table2]
319        value = [ 42.0 ]
320        [table3]
321        value = [ true ]
322        "#,
323        )
324        .unwrap();
325        let result = do_resolve!(toml => "table1.value.[0]");
326
327        assert!(result.is_ok());
328        let result = result.unwrap();
329
330        assert!(is_match!(result, Value::String(_)));
331        match result {
332            Value::String(ref s) => assert_eq!("Foo", s),
333            _ => panic!("What just happened?"),
334        }
335    }
336
337    static FRUIT_TABLE: &str = r#"
338    [[fruit.blah]]
339      name = "apple"
340
341      [fruit.blah.physical]
342        color = "red"
343        shape = "round"
344
345    [[fruit.blah]]
346      name = "banana"
347
348      [fruit.blah.physical]
349        color = "yellow"
350        shape = "bent"
351    "#;
352
353    #[test]
354    fn test_resolve_array_table_query_1() {
355        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
356        let result = do_resolve!(toml => "fruit.blah.[0].name");
357
358        assert!(result.is_ok());
359        let result = result.unwrap();
360
361        assert!(is_match!(result, Value::String(_)));
362        match result {
363            Value::String(ref s) => assert_eq!("apple", s),
364            _ => panic!("What just happened?"),
365        }
366    }
367
368    #[test]
369    fn test_resolve_array_table_query_2() {
370        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
371        let result = do_resolve!(toml => "fruit.blah.[0].physical");
372
373        assert!(result.is_ok());
374        let result = result.unwrap();
375
376        assert!(is_match!(result, Value::Table(_)));
377        match result {
378            Value::Table(ref tab) => {
379                match tab.get("color") {
380                    Some(&Value::String(ref s)) => assert_eq!("red", s),
381                    _ => unreachable!(),
382                }
383                match tab.get("shape") {
384                    Some(&Value::String(ref s)) => assert_eq!("round", s),
385                    _ => unreachable!(),
386                }
387            }
388            _ => panic!("What just happened?"),
389        }
390    }
391
392    #[test]
393    fn test_resolve_query_on_result() {
394        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
395        let result = do_resolve!(toml => "fruit.blah.[1].physical");
396
397        assert!(result.is_ok());
398        let result = result.unwrap();
399
400        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
401        let result = resolve(result, &tokens);
402
403        assert!(result.is_ok());
404        let result = result.unwrap();
405
406        assert!(is_match!(result, Value::String(_)));
407        match result {
408            Value::String(ref s) => assert_eq!("yellow", s),
409            _ => panic!("What just happened?"),
410        }
411    }
412
413    #[test]
414    fn test_resolve_query_empty_table() {
415        let mut toml = toml_from_str(
416            r#"
417        [example]
418        "#,
419        )
420        .unwrap();
421        let result = do_resolve!(toml => "example");
422
423        assert!(result.is_ok());
424        let result = result.unwrap();
425
426        assert!(is_match!(result, Value::Table(_)));
427        match result {
428            Value::Table(ref t) => assert!(t.is_empty()),
429            _ => panic!("What just happened?"),
430        }
431    }
432
433    #[test]
434    fn test_resolve_query_member_of_empty_table() {
435        let mut toml = toml_from_str("").unwrap();
436        let result = do_resolve!(toml => "example.foo");
437
438        assert!(result.is_ok());
439        let result = result.unwrap();
440        assert!(is_match!(result, Value::Table(_)));
441        match result {
442            Value::Table(ref t) => assert!(t.is_empty()),
443            _ => panic!("What just happened?"),
444        }
445    }
446
447    #[test]
448    fn test_resolve_query_index_in_table() {
449        let mut toml = toml_from_str("").unwrap();
450        let result = do_resolve!(toml => "example.[0]");
451
452        // TODO: Array creating is not yet implemented properly
453        assert!(result.is_err());
454
455        //assert!(result.is_ok());
456        //let result = result.unwrap();
457
458        //assert!(is_match!(result, Value::Array(_)));
459        //match result {
460        //    Value::Array(ref a) => assert!(a.is_empty()),
461        //    _                        => panic!("What just happened?"),
462        //}
463    }
464
465    #[test]
466    fn test_resolve_query_identifier_in_array() {
467        let mut toml = toml_from_str("").unwrap();
468        let result = do_resolve!(toml => "example.foo.bar");
469
470        assert!(result.is_ok());
471        let result = result.unwrap();
472
473        match result {
474            Value::Table(ref t) => assert!(t.is_empty()),
475            _ => panic!("What just happened?"),
476        }
477    }
478
479    #[test]
480    fn test_resolve_query_value_as_table() {
481        let mut toml = toml_from_str("").unwrap();
482        let result = do_resolve!(toml => "example.foo.bar");
483
484        assert!(result.is_ok());
485        let result = result.unwrap();
486
487        match result {
488            Value::Table(ref t) => assert!(t.is_empty()),
489            _ => panic!("What just happened?"),
490        }
491    }
492
493    #[test]
494    fn test_resolve_query_value_as_array() {
495        let mut toml = toml_from_str("").unwrap();
496        let result = do_resolve!(toml => "example.foo.[0]");
497
498        // TODO: Array creating is not yet implemented properly
499        assert!(result.is_err());
500
501        //assert!(result.is_ok());
502        //let result = result.unwrap();
503
504        //match result {
505        //    Value::Array(ref a) => assert!(a.is_empty()),
506        //    _                        => panic!("What just happened?"),
507        //}
508    }
509}