1extern crate pest;
2
3mod path;
4mod utils;
5pub mod yaml;
6
7pub use path::YamlPath;
8pub use yaml::{DocumentResult, YamlResult};
9
10#[cfg(test)]
11mod tests {
12 use super::yaml::parse_yaml_file;
13 use super::yaml::{AliasedYaml, Yaml, YamlInsert};
14 use super::YamlPath;
15 use crate::yaml::{DoubleQuotedStringPart, MyVec};
16
17 #[test]
18 fn basic() {
19 let inp = r#"---
20# test
21k: &anch test
22# hi
23v: &ok
24 - &ok2 test
25# comment
26 - *test
27v: test # maybe
28k: [5, 10, "e"]
29variable_groups: []
30double_quoted: "test
31
32now a space \n vs \\n
33"
34single_quoted: 'test escaping ''
35
36now a space \n vs \\n
37'
38l:
39 - 'sqrt( {d}^({r*n+m}m+{p}) * x^({r*a+p}) * y^{b*r+q} )'
40 - Read the values of the cyclometric numbers off of the circle. <br/> {gon_circle}
41
42# after
43"#;
44 let parsed = parse_yaml_file(inp);
45 insta::assert_debug_snapshot!(parsed);
46 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
47 }
48
49 #[test]
50 fn inline_hash() {
51 let inp = r#"---
52field: &test {}
53item: {}
54item2: { key: "value", other: [a, b]}
55other: # check
56 combine: { key: { key: [l, {a: "b"}]} }
57 item: {}
58"#;
59 let parsed = parse_yaml_file(inp);
60 insta::assert_debug_snapshot!(parsed);
61 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
62 }
63
64 #[test]
65 fn block_delimiters_in_text() {
66 let inp = r#"---
67content: When you press <em>Submit part</em>.
68abs: An absolute value is written as |x|
69"#;
70 let parsed = parse_yaml_file(inp);
71 insta::assert_debug_snapshot!(parsed);
72 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
73 }
74
75 #[test]
76 fn array_delimiters_in_text() {
77 let inp = r#"---
78default_value: random(-9 .. 9 except [0, a])
79"#;
80 let parsed = parse_yaml_file(inp);
81 insta::assert_debug_snapshot!(parsed);
82 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
83 }
84
85 #[test]
86 fn comments_before_start() {
87 let inp = r#"
88# a comment
89# another one
90
91---
92x: 5
93
94 "#;
95
96 let parsed = parse_yaml_file(inp);
97 insta::assert_debug_snapshot!(parsed);
98 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
99 }
100
101 #[test]
102 fn elab() {
103 let inp = r#"---
104# test
105k: &anch test
106# hi
107
108v: &ok
109 - &ok2 test
110# comment
111 - *test # an anchor
112v: test # maybe
113k: &kk [5, 10, "e"]
114# after
115y: - x: 5
116 z: &zfield 6 # yess
117
118 - n: 8
119
120 - j
121# between
122z: - *kk
123
124 - ke
125k: - - j
126 - k
127
128 - l
129 - - m
130 - z: k
131
132
133 t: - m: l # comment
134 p: 4 # comment
135 - m # after m
136# wow
137# something else
138"#;
139 let parsed = parse_yaml_file(inp);
140 insta::assert_debug_snapshot!(parsed);
141 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
142 }
143
144 #[test]
145 fn block_scalars() {
146 let inp = r#"---
148 newlines: &newline |
149 Several lines of text,
150 with some "quotes" of various 'types',
151 and also a blank line:
152
153 and some text with
154 extra indentation
155 on the next line,
156 plus another line at the end.
157
158 folded: &folded >
159 Several lines of text,
160 with some "quotes" of various 'types',
161 and also a blank line:
162
163 and some text with
164 extra indentation
165 on the next line,
166 plus another line at the end.
167
168
169
170 test: 5"#;
171
172 let parsed = parse_yaml_file(inp);
173 insta::assert_debug_snapshot!(parsed);
174 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
175 }
176
177 #[test]
178 fn block_scalars_with_chomping() {
179 let inp = r#"---
181 newlines: &newline |-
182 Several lines of text,
183 with some "quotes" of various 'types',
184 and also a blank line:
185
186 and some text with
187 extra indentation
188 on the next line,
189 plus another line at the end.
190
191 folded: &folded >+
192 Several lines of text,
193 with some "quotes" of various 'types',
194 and also a blank line:
195
196 and some text with
197 extra indentation
198 on the next line,
199 plus another line at the end.
200
201
202
203 test: 5"#;
204
205 let parsed = parse_yaml_file(inp);
206 insta::assert_debug_snapshot!(parsed);
207 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
208 }
209
210 #[test]
211 fn block_scalars_with_indent() {
212 let inp = r#"---
214 newlines: &newline |10
215 Several lines of text,
216 with some "quotes" of various 'types',
217 and also a blank line:
218
219 and some text with
220 extra indentation
221 on the next line,
222 plus another line at the end.
223
224 folded: &folded >10
225 Several lines of text,
226 with some "quotes" of various 'types',
227 and also a blank line:
228
229 and some text with
230 extra indentation
231 on the next line,
232 plus another line at the end.
233
234
235
236 test: 5"#;
237
238 let parsed = parse_yaml_file(inp);
239 insta::assert_debug_snapshot!(parsed);
240 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
241 }
242
243 #[test]
244 fn block_scalars_with_indent_and_empty_lines() {
245 let inp = r#"---
247 newlines: &newline |10
248 Several lines of text,
249 with some "quotes" of various 'types',
250 and also a blank line:
251
252 and some text with
253 extra indentation
254 on the next line,
255 plus another line at the end.
256
257 folded: &folded >10
258 Several lines of text,
259 with some "quotes" of various 'types',
260 and also a blank line:
261
262 and some text with
263 extra indentation
264 on the next line,
265 plus another line at the end.
266
267
268
269 test: 5"#;
270
271 let parsed = parse_yaml_file(inp);
272 insta::assert_debug_snapshot!(parsed);
273 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
274 }
275
276 #[test]
277 fn block_scalars_deeper() {
278 let inp = r#"---
279parts:
280 -
281 type: gapfill
282 marks: 0
283 prompt:
284 content: test
285 other: |
286 8
287 10"#;
288
289 let parsed = parse_yaml_file(inp);
290 insta::assert_debug_snapshot!(parsed);
291 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
292 }
293
294 #[test]
295 fn commas_in_unquoted_string() {
296 let inp = r#"---
297inline_array: [test, 5, hi]
298s: &key test, 5, hi
299"#;
300 let parsed = parse_yaml_file(inp);
301 insta::assert_debug_snapshot!(parsed);
302 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
303 }
304
305 #[test]
306 fn alternative_hash_comment_between_first_and_second() {
307 let inp = r#"---
308parts:
309- type: choose_one
310 # prompt: "file:statement-wrong.html"
311 answer_data: tmp
312 "#;
313
314 let parsed = parse_yaml_file(inp);
315 insta::assert_debug_snapshot!(parsed);
316 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
317 }
318
319 #[test]
320 fn comment_before_first_key() {
321 let inp = r#"---
322default_value:
323 content:
324 # NOTE: the 'original' non-shuffled array advr is used, rather than x_advr !!!
325 # Better advice to be written ...? This one does not correctly extend to users of this template!!
326 nl: "Gebruik het merkwaardig product {if(a*b>0, advr[0], advr[1])}"
327 en: "Use the special product {if(a*b>0, advr[0], advr[1])}""#;
328 let parsed = parse_yaml_file(inp);
329 insta::assert_debug_snapshot!(parsed);
330 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
331 }
332
333 #[test]
334 fn insert_hash_top() {
335 let inp = r#"---
336inline_array: [test, 5, hi]
337s: &key test, 5, hi
338item:
339 key: value
340"#;
341 let mut parsed = parse_yaml_file(inp).unwrap();
342 let path: YamlPath = "new".parse().unwrap();
343 let data = AliasedYaml {
344 alias: None,
345 value: Yaml::UnquotedString("value".to_string()),
346 };
347 assert_eq!(1, parsed.insert_into_hash(&path, &data, false));
348
349 let out = r#"---
350inline_array: [test, 5, hi]
351s: &key test, 5, hi
352item:
353 key: value
354new: value
355"#;
356
357 let parsed_out = parse_yaml_file(out).unwrap();
358 assert_eq!(parsed_out, parsed);
359 }
360
361 #[test]
362 fn insert_hash() {
363 let inp = r#"---
364inline_array: [test, 5, hi]
365s: &key test, 5, hi
366item:
367 key: value
368"#;
369 let mut parsed = parse_yaml_file(inp).unwrap();
370 let path: YamlPath = "item.key2".parse().unwrap();
371 let data = AliasedYaml {
372 alias: None,
373 value: Yaml::DoubleQuotedString(vec![DoubleQuotedStringPart::String(
374 "test".to_string(),
375 )]),
376 };
377 assert_eq!(1, parsed.insert_into_hash(&path, &data, false));
378
379 let out = r#"---
380inline_array: [test, 5, hi]
381s: &key test, 5, hi
382item:
383 key: value
384 key2: "test"
385"#;
386
387 let parsed_out = parse_yaml_file(out).unwrap();
388 assert_eq!(parsed_out, parsed);
389
390 let path: YamlPath = "item.key2".parse().unwrap();
391 let data = AliasedYaml {
392 alias: None,
393 value: Yaml::DoubleQuotedString(vec![DoubleQuotedStringPart::String(
394 "test2".to_string(),
395 )]),
396 };
397 assert_eq!(0, parsed.insert_into_hash(&path, &data, false));
398 assert_eq!(parsed_out, parsed);
399 assert_eq!(1, parsed.insert_into_hash(&path, &data, true));
400 let out = r#"---
401inline_array: [test, 5, hi]
402s: &key test, 5, hi
403item:
404 key: value
405 key2: "test2"
406"#;
407
408 let parsed_out = parse_yaml_file(out).unwrap();
409 assert_eq!(parsed_out, parsed);
410 }
411
412 #[test]
413 fn rename_field() {
414 let inp = r#"---
415inline_array: [test, 5, hi]
416s: &key test, 5, hi
417item:
418 key: value
419"#;
420 let mut parsed = parse_yaml_file(inp).unwrap();
421 let path: YamlPath = "item.key".parse().unwrap();
422 assert_eq!(1, parsed.rename_field(&path, "newkey".to_string()));
423
424 let out = r#"---
425inline_array: [test, 5, hi]
426s: &key test, 5, hi
427item:
428 newkey: value
429"#;
430
431 let parsed_out = parse_yaml_file(out).unwrap();
432 assert_eq!(parsed_out, parsed);
433 }
434
435 #[test]
436 fn rename_field_list_in_list() {
437 let inp = r#"---
438parts:
439 - type: gapfill
440 gaps:
441 - type: jme
442 old_name: value
443 - type: jme
444 old_name: value
445 - type: random
446 gaps:
447 - type: jme
448 old_name: value
449 - type: jme
450 old_name: value
451 - type: gapfill
452 gaps:
453 - type: jme
454 old_name: value
455 - type: jme
456 old_name: value
457"#;
458 let mut parsed = parse_yaml_file(inp).unwrap();
459 let path: YamlPath = "parts[*]|type=gapfill.gaps[*].old_name".parse().unwrap();
460 assert_eq!(4, parsed.rename_field(&path, "new_name".to_string()));
461
462 let out = r#"---
463parts:
464 - type: gapfill
465 gaps:
466 - type: jme
467 new_name: value
468 - type: jme
469 new_name: value
470 - type: random
471 gaps:
472 - type: jme
473 old_name: value
474 - type: jme
475 old_name: value
476 - type: gapfill
477 gaps:
478 - type: jme
479 new_name: value
480 - type: jme
481 new_name: value
482"#;
483
484 let parsed_out = parse_yaml_file(out).unwrap();
485 assert_eq!(parsed_out, parsed);
486 }
487
488 #[test]
489 fn to_object() {
490 let inp = r#"---
491inline_array: [test, 5, hi]
492s: &key test, 5, hi
493item:
494 key: value
495"#;
496 let mut parsed = parse_yaml_file(inp).unwrap();
497 let path: YamlPath = "item.key".parse().unwrap();
498 assert_eq!(1, parsed.to_object(&path, "subkey".to_string()));
499
500 let out = r#"---
501inline_array: [test, 5, hi]
502s: &key test, 5, hi
503item:
504 key:
505 subkey: value
506"#;
507
508 let parsed_out = parse_yaml_file(out).unwrap();
509 assert_eq!(parsed_out, parsed);
510 }
511 #[test]
512 fn to_object_with_condition() {
513 let inp = r#"---
514inline_array: [test, 5, hi]
515s: &key test, 5, hi
516item:
517 key: value
518"#;
519 let mut parsed = parse_yaml_file(inp).unwrap();
520 let path: YamlPath = "item.key|key=value".parse().unwrap();
521 assert_eq!(1, parsed.to_object(&path, "subkey".to_string()));
522
523 let out = r#"---
524inline_array: [test, 5, hi]
525s: &key test, 5, hi
526item:
527 key:
528 subkey: value
529"#;
530
531 let parsed_out = parse_yaml_file(out).unwrap();
532 assert_eq!(parsed_out, parsed);
533
534 let mut parsed = parse_yaml_file(inp).unwrap();
535 let path: YamlPath = "item.key|key=other".parse().unwrap();
536 assert_eq!(0, parsed.to_object(&path, "subkey".to_string()));
537 assert_eq!(parse_yaml_file(inp).unwrap(), parsed);
538 }
539 #[test]
540 fn remove_from_hash() {
541 let inp = r#"---
542inline_array: [test, 5, hi]
543s: &key test, 5, hi
544item:
545 key: value
546"#;
547 let mut parsed = parse_yaml_file(inp).unwrap();
548 let path: YamlPath = "item".parse().unwrap();
549 assert_eq!(1, parsed.remove_from_hash(&path));
550
551 let out = r#"---
552inline_array: [test, 5, hi]
553s: &key test, 5, hi
554"#;
555
556 let parsed_out = parse_yaml_file(out).unwrap();
557 assert_eq!(parsed_out, parsed);
558 }
559 #[test]
560 fn remove_from_hash_deeper() {
561 let inp = r#"---
562inline_array: [test, 5, hi]
563s: &key test, 5, hi
564item:
565 key:
566 item: 5
567 item2: 6
568"#;
569 let mut parsed = parse_yaml_file(inp).unwrap();
570 let path: YamlPath = "item.key.item".parse().unwrap();
571 assert_eq!(1, parsed.remove_from_hash(&path));
572 let out = r#"---
573inline_array: [test, 5, hi]
574s: &key test, 5, hi
575item:
576 key:
577 item2: 6
578"#;
579
580 let parsed_out = parse_yaml_file(out).unwrap();
581 assert_eq!(parsed_out, parsed);
582 }
583 #[test]
584 fn remove_from_hash_deeper_with_condition() {
585 let inp = r#"---
586type: test
587inline_array: [test, 5, hi]
588s: &key test, 5, hi
589item:
590 key:
591 item: 5
592 item2: 6
593"#;
594 let mut parsed = parse_yaml_file(inp).unwrap();
595 let path: YamlPath = "item|type=test.key.item".parse().unwrap();
596 assert_eq!(1, parsed.remove_from_hash(&path));
597 let out = r#"---
598type: test
599inline_array: [test, 5, hi]
600s: &key test, 5, hi
601item:
602 key:
603 item2: 6
604"#;
605
606 let parsed_out = parse_yaml_file(out).unwrap();
607 assert_eq!(parsed_out, parsed);
608
609 let mut parsed = parse_yaml_file(inp).unwrap();
610 let path: YamlPath = "item|type=other.key.item".parse().unwrap();
611 assert_eq!(0, parsed.remove_from_hash(&path));
612 assert_eq!(parse_yaml_file(inp).unwrap(), parsed);
613 }
614 #[test]
615 fn move_to_subfield_in_root() {
616 let inp = r#"---
617a: vala
618b: valb
619c: valc
620d: vald
621e: vale
622"#;
623
624 let mut parsed = parse_yaml_file(inp).unwrap();
625 let path: YamlPath = "".parse().unwrap();
626 assert_eq!(
627 1,
628 parsed.move_to_subfield(
629 &path,
630 "sub".to_string(),
631 vec!["e".to_string(), "b".to_string(), "d".to_string()]
632 )
633 );
634 let out = r#"---
635a: vala
636c: valc
637sub:
638 e: vale
639 b: valb
640 d: vald
641"#;
642
643 let parsed_out = parse_yaml_file(out).unwrap();
644 assert_eq!(parsed_out, parsed);
645 }
646 #[test]
647 fn move_to_subfield() {
648 let inp = r#"---
649k:
650 a: vala
651 b: valb
652 c: valc
653 d: vald
654 e: vale
655"#;
656
657 let mut parsed = parse_yaml_file(inp).unwrap();
658 let path: YamlPath = "k".parse().unwrap();
659 assert_eq!(
660 1,
661 parsed.move_to_subfield(
662 &path,
663 "sub".to_string(),
664 vec!["e".to_string(), "b".to_string(), "d".to_string()]
665 )
666 );
667 let out = r#"---
668k:
669 a: vala
670 c: valc
671 sub:
672 e: vale
673 b: valb
674 d: vald
675"#;
676
677 let parsed_out = parse_yaml_file(out).unwrap();
678 assert_eq!(parsed_out, parsed);
679 }
680 #[test]
681 fn move_to_existing_subfield() {
682 let inp = r#"---
683k:
684 a: vala
685 b: valb
686 c: valc
687 d: vald
688 e: vale
689 l:
690 z: valz
691"#;
692
693 let mut parsed = parse_yaml_file(inp).unwrap();
694 let path: YamlPath = "k".parse().unwrap();
695 assert_eq!(
696 1,
697 parsed.move_to_subfield(
698 &path,
699 "l".to_string(),
700 vec![
701 "e".to_string(),
702 "b".to_string(),
703 "d".to_string(),
704 "kk".to_string()
705 ]
706 )
707 );
708 let out = r#"---
709k:
710 a: vala
711 c: valc
712 l:
713 z: valz
714 e: vale
715 b: valb
716 d: vald
717"#;
718
719 let parsed_out = parse_yaml_file(out).unwrap();
720 assert_eq!(parsed_out, parsed);
721 }
722 #[test]
723 fn move_to_subfield_list() {
724 let inp = r#"---
725k:
726 - item1: test0
727 result:
728 yes: three
729 - item1: test1
730"#;
731
732 let mut parsed = parse_yaml_file(inp).unwrap();
733 let path: YamlPath = "k[*]".parse().unwrap();
734 assert_eq!(
735 2,
736 parsed.move_to_subfield(&path, "result".to_string(), vec!["item1".to_string(),])
737 );
738 let out = r#"---
739k:
740 - result:
741 yes: three
742 item1: test0
743 - result:
744 item1: test1
745"#;
746
747 let parsed_out = parse_yaml_file(out).unwrap();
748 assert_eq!(parsed_out, parsed);
749 }
750 #[test]
751 fn move_to_subfield_list_item() {
752 let inp = r#"---
753k:
754 - item1: test0
755 result:
756 yes: three
757 - item1: test1
758"#;
759
760 let mut parsed = parse_yaml_file(inp).unwrap();
761 let path: YamlPath = "k[0]".parse().unwrap();
762 assert_eq!(
763 1,
764 parsed.move_to_subfield(&path, "result".to_string(), vec!["item1".to_string(),])
765 );
766 let out = r#"---
767k:
768 - result:
769 yes: three
770 item1: test0
771 - item1: test1
772"#;
773
774 let parsed_out = parse_yaml_file(out).unwrap();
775 assert_eq!(parsed_out, parsed);
776 }
777 #[test]
778 fn move_to_subfield_list_filter() {
779 let inp = r#"---
780k:
781 - item1: test0
782 result:
783 yes: three
784 - item1: test1
785"#;
786
787 let mut parsed = parse_yaml_file(inp).unwrap();
788 let path: YamlPath = "k[*]|item1=test1".parse().unwrap();
789 assert_eq!(
790 1,
791 parsed.move_to_subfield(&path, "result".to_string(), vec!["item1".to_string(),])
792 );
793 let out = r#"---
794k:
795 - item1: test0
796 result:
797 yes: three
798 - result:
799 item1: test1
800"#;
801
802 let parsed_out = parse_yaml_file(out).unwrap();
803 assert_eq!(parsed_out, parsed);
804 }
805 #[test]
806 fn neg_number_in_list() {
807 let inp = r#"---
808vset_range:
809 - -10
810 - 10
811vset_range_points: 20
812"#;
813
814 let parsed = parse_yaml_file(inp);
815 insta::assert_debug_snapshot!(parsed);
816 insta::assert_display_snapshot!(parsed.unwrap().format().unwrap());
817 }
818 #[test]
819 fn move_to_map_with_field_as_key() {
820 let inp = r#"---
821k:
822 - item1:
823 variables:
824 # this specifies the test
825 test: value
826 ke:
827 # comments
828 definition: result
829 group: keep
830 # this one will be moved
831 other:
832 definition: result
833 group: different
834 k:
835 definition: value
836 group: ""
837 main:
838 definiton: field
839 group: other
840 other2:
841 definition: result2
842 group: different
843 - item2:
844 variables:
845 # this specifies the test
846 test: value
847 grouped_variables: anything
848"#;
849
850 let mut parsed = parse_yaml_file(inp).unwrap();
851 let path: YamlPath = "k[*].item1".parse().unwrap();
852 assert_eq!(
853 1,
854 parsed.move_to_map_with_field_as_key(
855 &path,
856 "variables".to_string(),
857 "group".to_string(),
858 "grouped_variables".to_string(),
859 vec!["keep".to_string()]
860 )
861 );
862 let out = r#"---
863k:
864- item1:
865 variables:
866 # this specifies the test
867 test: value
868 ke:
869 # comments
870 definition: result
871 k:
872 definition: value
873 grouped_variables:
874 different:
875 # this one will be moved
876 other:
877 definition: result
878 other2:
879 definition: result2
880 other:
881 main:
882 definiton: field
883- item2:
884 variables:
885 # this specifies the test
886 test: value
887 grouped_variables: anything
888"#;
889 println!("{}", parsed.format().unwrap());
890 let parsed_out = parse_yaml_file(out).unwrap();
891 assert_eq!(parsed_out, parsed);
892 }
893
894 #[test]
895 fn find() {
896 let inp = r#"---
897k:
898 - item1: test0
899 result:
900 yes: three
901 - item1: test1
902"#;
903
904 let mut parsed = parse_yaml_file(inp).unwrap();
905 let path: YamlPath = "k[*].item1".parse().unwrap();
906 assert_eq!(
907 MyVec(vec![
908 Yaml::UnquotedString("test0".to_string()),
909 Yaml::UnquotedString("test1".to_string()),
910 ]),
911 parsed.find_values(&path)
912 );
913 }
914}