1use std::ops::Index;
3
4use crate::error::{Error, Result};
5use crate::tokenizer::Token;
6use toml::Value;
7
8pub fn resolve<'doc>(
15 toml: &'doc Value,
16 tokens: &Token,
17 error_if_not_found: bool,
18) -> Result<Option<&'doc Value>> {
19 match toml {
20 Value::Table(ref t) => match tokens {
21 Token::Identifier { ref ident, .. } => match t.get(ident) {
22 None => {
23 if error_if_not_found {
24 Err(Error::IdentifierNotFoundInDocument(ident.to_owned()))
25 } else {
26 Ok(None)
27 }
28 }
29 Some(sub_document) => match tokens.next() {
30 Some(next) => resolve(sub_document, next, error_if_not_found),
31 None => Ok(Some(sub_document)),
32 },
33 },
34
35 Token::Index { idx, .. } => Err(Error::NoIndexInTable(*idx)),
36 },
37
38 Value::Array(ref ary) => match tokens {
39 Token::Index { idx, .. } => match tokens.next() {
40 Some(next) => resolve(ary.get(*idx).unwrap(), next, error_if_not_found),
41 None => {
42 if ary.get(*idx).is_none() {
43 Err(Error::IndexOutOfBounds(*idx, ary.len()))
44 } else {
45 Ok(Some(ary.index(*idx)))
46 }
47 }
48 },
49 Token::Identifier { ref ident, .. } => Err(Error::NoIdentifierInArray(ident.clone())),
50 },
51
52 _ => match tokens {
53 Token::Identifier { ref ident, .. } => Err(Error::QueryingValueAsTable(ident.clone())),
54
55 Token::Index { idx, .. } => Err(Error::QueryingValueAsArray(*idx)),
56 },
57 }
58}
59
60#[cfg(test)]
61mod test {
62 use super::resolve;
63 use crate::error::*;
64 use crate::tokenizer::*;
65 use toml::from_str as toml_from_str;
66 use toml::Value;
67
68 macro_rules! do_resolve {
69 ( $toml:ident => $query:expr ) => {
70 resolve(
71 &$toml,
72 &tokenize_with_seperator(&String::from($query), '.').unwrap(),
73 true,
74 )
75 };
76 }
77
78 #[test]
79 fn test_resolve_empty_toml_simple_query() {
80 let toml = toml_from_str("").unwrap();
81 let result = do_resolve!(toml => "example");
82
83 assert!(result.is_err());
84 let result = result.unwrap_err();
85
86 assert!(is_match!(result, Error::IdentifierNotFoundInDocument { .. }));
87 }
88
89 #[test]
90 fn test_resolve_present_bool() {
91 let toml = toml_from_str("example = true").unwrap();
92 let result = do_resolve!(toml => "example");
93
94 assert!(result.is_ok());
95 let result = result.unwrap();
96
97 assert!(result.is_some());
98 let result = result.unwrap();
99
100 assert!(is_match!(result, Value::Boolean(true)));
101 }
102
103 #[test]
104 fn test_resolve_present_integer() {
105 let toml = toml_from_str("example = 1").unwrap();
106 let result = do_resolve!(toml => "example");
107
108 assert!(result.is_ok());
109 let result = result.unwrap();
110
111 assert!(result.is_some());
112 let result = result.unwrap();
113
114 assert!(is_match!(result, Value::Integer(1)));
115 }
116
117 #[test]
118 fn test_resolve_present_float() {
119 let toml = toml_from_str("example = 1.0").unwrap();
120 let result = do_resolve!(toml => "example");
121
122 assert!(result.is_ok());
123 let result = result.unwrap();
124
125 assert!(result.is_some());
126 let result = result.unwrap();
127
128 assert!(is_match!(result, Value::Float(_)));
129 assert_eq!(result.as_float(), Some(1.0))
130 }
131
132 #[test]
133 fn test_resolve_present_string() {
134 let toml = toml_from_str("example = 'string'").unwrap();
135 let result = do_resolve!(toml => "example");
136
137 assert!(result.is_ok());
138 let result = result.unwrap();
139
140 assert!(result.is_some());
141 let result = result.unwrap();
142
143 assert!(is_match!(result, Value::String(_)));
144 match result {
145 Value::String(ref s) => assert_eq!("string", s),
146 _ => panic!("What just happened?"),
147 }
148 }
149
150 #[test]
151 fn test_resolve_present_array_bools() {
152 let toml = toml_from_str("example = [ true, false ]").unwrap();
153 let result = do_resolve!(toml => "example");
154
155 assert!(result.is_ok());
156 let result = result.unwrap();
157
158 assert!(result.is_some());
159 let result = result.unwrap();
160
161 assert!(is_match!(result, Value::Array(_)));
162 match result {
163 Value::Array(ref ary) => {
164 assert_eq!(ary[0], Value::Boolean(true));
165 assert_eq!(ary[1], Value::Boolean(false));
166 }
167 _ => panic!("What just happened?"),
168 }
169 }
170
171 #[test]
172 fn test_resolve_present_array_integers() {
173 let toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
174 let result = do_resolve!(toml => "example");
175
176 assert!(result.is_ok());
177 let result = result.unwrap();
178
179 assert!(result.is_some());
180 let result = result.unwrap();
181
182 assert!(is_match!(result, Value::Array(_)));
183 match result {
184 Value::Array(ref ary) => {
185 assert_eq!(ary[0], Value::Integer(1));
186 assert_eq!(ary[1], Value::Integer(1337));
187 }
188 _ => panic!("What just happened?"),
189 }
190 }
191
192 #[test]
193 fn test_resolve_present_array_floats() {
194 let toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
195 let result = do_resolve!(toml => "example");
196
197 assert!(result.is_ok());
198 let result = result.unwrap();
199
200 assert!(result.is_some());
201 let result = result.unwrap();
202
203 assert!(is_match!(result, Value::Array(_)));
204 match result {
205 Value::Array(ref ary) => {
206 assert!(is_match!(ary[0], Value::Float(_)));
207 assert_eq!(ary[0].as_float(), Some(1.0));
208 assert!(is_match!(ary[1], Value::Float(_)));
209 assert_eq!(ary[1].as_float(), Some(133.25));
210 }
211 _ => panic!("What just happened?"),
212 }
213 }
214
215 #[test]
216 fn test_resolve_array_index_query_1() {
217 let toml = toml_from_str("example = [ 1 ]").unwrap();
218 let result = do_resolve!(toml => "example.[0]");
219
220 assert!(result.is_ok());
221 let result = result.unwrap();
222
223 assert!(result.is_some());
224 let result = result.unwrap();
225
226 assert!(is_match!(result, Value::Integer(1)));
227 }
228
229 #[test]
230 fn test_resolve_array_index_query_2() {
231 let toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
232 let result = do_resolve!(toml => "example.[4]");
233
234 assert!(result.is_ok());
235 let result = result.unwrap();
236
237 assert!(result.is_some());
238 let result = result.unwrap();
239
240 assert!(is_match!(result, Value::Integer(5)));
241 }
242
243 #[test]
244 fn test_resolve_table_element_query() {
245 let toml = toml_from_str(
246 r#"
247 [table]
248 value = 42
249 "#,
250 )
251 .unwrap();
252 let result = do_resolve!(toml => "table.value");
253
254 assert!(result.is_ok());
255 let result = result.unwrap();
256
257 assert!(result.is_some());
258 let result = result.unwrap();
259
260 assert!(is_match!(result, Value::Integer(42)));
261 }
262
263 #[test]
264 fn test_resolve_table_with_many_elements_element_query() {
265 let toml = toml_from_str(
266 r#"
267 [table]
268 value1 = 42
269 value2 = 43
270 value3 = 44
271 value4 = 45
272 value5 = 46
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!(result.is_some());
282 let result = result.unwrap();
283
284 assert!(is_match!(result, Value::Integer(42)));
285 }
286
287 #[test]
288 fn test_resolve_table_array_query() {
289 let toml = toml_from_str(
290 r#"
291 [table]
292 value1 = [ 42.0, 50.0 ]
293 "#,
294 )
295 .unwrap();
296 let result = do_resolve!(toml => "table.value1");
297
298 assert!(result.is_ok());
299 let result = result.unwrap();
300
301 assert!(result.is_some());
302 let result = result.unwrap();
303
304 assert!(is_match!(result, Value::Array(_)));
305 match result {
306 Value::Array(ref ary) => {
307 assert!(is_match!(ary[0], Value::Float(_)));
308 assert_eq!(ary[0].as_float(), Some(42.0));
309 assert!(is_match!(ary[1], Value::Float(_)));
310 assert_eq!(ary[1].as_float(), Some(50.0));
311 }
312 _ => panic!("What just happened?"),
313 }
314 }
315
316 #[test]
317 fn test_resolve_table_array_element_query() {
318 let toml = toml_from_str(
319 r#"
320 [table]
321 value1 = [ 42 ]
322 "#,
323 )
324 .unwrap();
325 let result = do_resolve!(toml => "table.value1.[0]");
326
327 assert!(result.is_ok());
328 let result = result.unwrap();
329
330 assert!(result.is_some());
331 let result = result.unwrap();
332
333 assert!(is_match!(result, Value::Integer(42)));
334 }
335
336 #[test]
337 fn test_resolve_multi_table_query() {
338 let toml = toml_from_str(
339 r#"
340 [table0]
341 value = [ 1 ]
342 [table1]
343 value = [ "Foo" ]
344 [table2]
345 value = [ 42.0 ]
346 [table3]
347 value = [ true ]
348 "#,
349 )
350 .unwrap();
351 let result = do_resolve!(toml => "table1.value.[0]");
352
353 assert!(result.is_ok());
354 let result = result.unwrap();
355
356 assert!(result.is_some());
357 let result = result.unwrap();
358
359 assert!(is_match!(result, Value::String(_)));
360 match result {
361 Value::String(ref s) => assert_eq!("Foo", s),
362 _ => panic!("What just happened?"),
363 }
364 }
365
366 static FRUIT_TABLE: &str = r#"
367 [[fruit.blah]]
368 name = "apple"
369
370 [fruit.blah.physical]
371 color = "red"
372 shape = "round"
373
374 [[fruit.blah]]
375 name = "banana"
376
377 [fruit.blah.physical]
378 color = "yellow"
379 shape = "bent"
380 "#;
381
382 #[test]
383 fn test_resolve_array_table_query_1() {
384 let toml = toml_from_str(FRUIT_TABLE).unwrap();
385 let result = do_resolve!(toml => "fruit.blah.[0].name");
386
387 assert!(result.is_ok());
388 let result = result.unwrap();
389
390 assert!(result.is_some());
391 let result = result.unwrap();
392
393 assert!(is_match!(result, Value::String(_)));
394 match result {
395 Value::String(ref s) => assert_eq!("apple", s),
396 _ => panic!("What just happened?"),
397 }
398 }
399
400 #[test]
401 fn test_resolve_array_table_query_2() {
402 let toml = toml_from_str(FRUIT_TABLE).unwrap();
403 let result = do_resolve!(toml => "fruit.blah.[0].physical");
404
405 assert!(result.is_ok());
406 let result = result.unwrap();
407
408 assert!(result.is_some());
409 let result = result.unwrap();
410
411 assert!(is_match!(result, Value::Table(_)));
412 match result {
413 Value::Table(ref tab) => {
414 match tab.get("color") {
415 Some(&Value::String(ref s)) => assert_eq!("red", s),
416 _ => unreachable!(),
417 }
418 match tab.get("shape") {
419 Some(&Value::String(ref s)) => assert_eq!("round", s),
420 _ => unreachable!(),
421 }
422 }
423 _ => panic!("What just happened?"),
424 }
425 }
426
427 #[test]
428 fn test_resolve_query_on_result() {
429 let toml = toml_from_str(FRUIT_TABLE).unwrap();
430 let result = do_resolve!(toml => "fruit.blah.[1].physical");
431
432 assert!(result.is_ok());
433 let result = result.unwrap();
434
435 assert!(result.is_some());
436 let result = result.unwrap();
437
438 let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
439 let result = resolve(result, &tokens, true);
440
441 assert!(result.is_ok());
442 let result = result.unwrap();
443
444 assert!(result.is_some());
445 let result = result.unwrap();
446
447 assert!(is_match!(result, Value::String(_)));
448 match result {
449 Value::String(ref s) => assert_eq!("yellow", s),
450 _ => panic!("What just happened?"),
451 }
452 }
453
454 #[test]
455 fn test_resolve_query_empty_table() {
456 let toml = toml_from_str(
457 r#"
458 [example]
459 "#,
460 )
461 .unwrap();
462 let result = do_resolve!(toml => "example");
463
464 assert!(result.is_ok());
465 let result = result.unwrap();
466
467 assert!(result.is_some());
468 let result = result.unwrap();
469
470 assert!(is_match!(result, Value::Table(_)));
471 match result {
472 Value::Table(ref t) => assert!(t.is_empty()),
473 _ => panic!("What just happened?"),
474 }
475 }
476
477 #[test]
478 fn test_resolve_query_member_of_empty_table() {
479 let toml = toml_from_str(
480 r#"
481 [example]
482 "#,
483 )
484 .unwrap();
485 let result = do_resolve!(toml => "example.foo");
486
487 assert!(result.is_err());
488 let result = result.unwrap_err();
489
490 assert!(is_match!(result, Error::IdentifierNotFoundInDocument { .. }));
491 }
492
493 #[test]
494 fn test_resolve_query_index_in_table() {
495 let toml = toml_from_str(
496 r#"
497 [example]
498 "#,
499 )
500 .unwrap();
501 let result = do_resolve!(toml => "example.[0]");
502
503 assert!(result.is_err());
504 let result = result.unwrap_err();
505
506 assert!(is_match!(result, Error::NoIndexInTable { .. }));
507 }
508
509 #[test]
510 fn test_resolve_query_identifier_in_array() {
511 let toml = toml_from_str(
512 r#"
513 [example]
514 foo = [ 1, 2, 3 ]
515 "#,
516 )
517 .unwrap();
518 let result = do_resolve!(toml => "example.foo.bar");
519
520 assert!(result.is_err());
521 let result = result.unwrap_err();
522
523 assert!(is_match!(result, Error::NoIdentifierInArray { .. }));
524 }
525
526 #[test]
527 fn test_resolve_query_value_as_table() {
528 let toml = toml_from_str(
529 r#"
530 [example]
531 foo = 1
532 "#,
533 )
534 .unwrap();
535 let result = do_resolve!(toml => "example.foo.bar");
536
537 assert!(result.is_err());
538 let result = result.unwrap_err();
539
540 assert!(is_match!(result, Error::QueryingValueAsTable { .. }));
541 }
542
543 #[test]
544 fn test_resolve_query_value_as_array() {
545 let toml = toml_from_str(
546 r#"
547 [example]
548 foo = 1
549 "#,
550 )
551 .unwrap();
552 let result = do_resolve!(toml => "example.foo.[0]");
553
554 assert!(result.is_err());
555 let result = result.unwrap_err();
556
557 assert!(is_match!(result, Error::QueryingValueAsArray { .. }));
558 }
559
560 #[test]
561 fn test_indexing_out_of_bounds() {
562 let toml = toml_from_str(
563 r#"
564 [example]
565 foo = [ 1, 2, 3 ]
566 "#,
567 )
568 .unwrap();
569 let result = do_resolve!(toml => "example.foo.[12]");
570
571 assert!(result.is_err());
572 let result = result.unwrap_err();
573
574 assert!(is_match!(result, Error::IndexOutOfBounds { .. }));
575 }
576
577 #[test]
578 fn test_indexing_out_of_bounds_edgecase_1() {
579 let toml = toml_from_str(
580 r#"
581 [example]
582 foo = []
583 "#,
584 )
585 .unwrap();
586 let result = do_resolve!(toml => "example.foo.[0]");
587
588 assert!(result.is_err());
589 let result = result.unwrap_err();
590
591 assert!(is_match!(result, Error::IndexOutOfBounds { .. }));
592 }
593
594 #[test]
595 fn test_indexing_out_of_bounds_edgecase_2() {
596 let toml = toml_from_str(
597 r#"
598 [example]
599 foo = [ 1 ]
600 "#,
601 )
602 .unwrap();
603 let result = do_resolve!(toml => "example.foo.[1]");
604
605 assert!(result.is_err());
606 let result = result.unwrap_err();
607
608 assert!(is_match!(result, Error::IndexOutOfBounds { .. }));
609 }
610}