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