1 module toml_foolery.decode.parse_toml_test;
2 
3 import std.conv;
4 import std.datetime;
5 
6 import toml_foolery.attributes.toml_name;
7 import toml_foolery.decode.parse_toml;
8 import toml_foolery.decode.exceptions;
9 
10 
11 version (unittest)
12 {
13     import std.array : staticArray;
14     import std.datetime;
15 
16     import exceeds_expectations;
17 }
18 
19 
20 @("key re-declared as table from TOML readme")
21 unittest
22 {
23     struct S
24     {
25         struct Fruit { string apple; }
26 
27         Fruit fruit;
28     }
29 
30     try
31     {
32         S s = parseToml!S(`
33             [fruit]
34             apple = "red"
35 
36             [fruit.apple]
37             texture = "smooth"
38 
39         `);
40         assert(false, "Expected a TomlDecodingException to be thrown.");
41     }
42     catch (TomlDecodingException e)
43     {
44         // As expected
45     }
46 }
47 
48 @("table re-declared as key")
49 unittest
50 {
51     struct S
52     {
53         struct Fruit
54         {
55             struct Apple { string texture; }
56             Apple apple;
57         }
58 
59         Fruit fruit;
60     }
61 
62     try
63     {
64         S s = parseToml!S(`
65             [fruit.apple]
66             texture = "smooth"
67 
68             [fruit]
69             apple = "red"
70         `);
71         assert(false, "Expected a TomlDecodingException to be thrown.");
72     }
73     catch (TomlDecodingException e)
74     {
75         // As expected
76     }
77 }
78 
79 @("super-table declared after sub-tables is ok (from TOML readme)")
80 unittest
81 {
82     struct S
83     {
84         struct X
85         {
86             struct Y
87             {
88                 struct Z
89                 {
90                     struct W
91                     {
92                         int test1;
93                     }
94                     W w;
95                 }
96                 Z z;
97             }
98             Y y; // y, delilah?
99             int test2;
100         }
101         X x;
102     }
103 
104     S s = parseToml!S(`
105         # [x] you
106         # [x.y] don't
107         # [x.y.z] need these
108         [x.y.z.w] # for this to work
109         test1 = 1
110 
111         [x] # defining a super-table afterwards is ok
112         test2 = 2
113     `);
114 
115     expect(s.x.y.z.w.test1).toEqual(1);
116     expect(s.x.test2).toEqual(2);
117 }
118 
119 @("Simple Integer -> int")
120 unittest
121 {
122     struct S
123     {
124         int myInt;
125     }
126 
127     S result = parseToml!S("myInt = 5123456");
128 
129     expect(result.myInt).toEqual(5_123_456);
130 }
131 
132 @("Hex Integer -> long")
133 unittest
134 {
135     struct S
136     {
137         long hex;
138     }
139 
140     S result = parseToml!S("hex = 0xbeadface");
141 
142     expect(result.hex).toEqual(0xbeadface);
143 }
144 
145 @("Binary Integer -> ubyte")
146 unittest
147 {
148     struct S
149     {
150         ubyte bin;
151     }
152 
153     S result = parseToml!S("bin = 0b00110010");
154 
155     expect(result.bin).toEqual(50);
156 }
157 
158 @("Octal Integer -> short")
159 unittest
160 {
161     struct S
162     {
163         short oct;
164     }
165 
166     S result = parseToml!S("oct = 0o501");
167 
168     expect(result.oct).toEqual(321);
169 }
170 
171 @("Integers with underscores (all bases)")
172 unittest
173 {
174     struct S
175     {
176         int a;
177         int b;
178         int c;
179         int d;
180     }
181 
182     S result = parseToml!S(`
183     a = 1_000
184     b = 0x0000_00ff
185     c = 0o7_7_7
186     d = 0b0_0_0_1_0_0_0_1
187     `);
188 
189     expect(result.a).toEqual(1000);
190     expect(result.b).toEqual(255);
191     expect(result.c).toEqual(511);
192     expect(result.d).toEqual(17);
193 }
194 
195 @("Floating Point -> float")
196 unittest
197 {
198     struct S
199     {
200         float f;
201     }
202 
203     S result = parseToml!S("f = 1.1234");
204 
205     expect(result.f).toApproximatelyEqual(1.1234);
206 }
207 
208 @("Floating Point -> real")
209 unittest
210 {
211     struct S
212     {
213         real r;
214     }
215 
216     S result = parseToml!S("r = 12_232.008_2");
217 
218     expect(result.r).toApproximatelyEqual(12_232.0082);
219 }
220 
221 @("Floating Point -> double")
222 unittest
223 {
224     struct S
225     {
226         real d;
227     }
228 
229     S result = parseToml!S("d = -3.141_6e-01");
230 
231     expect(result.d).toApproximatelyEqual(-3.141_6e-01);
232 }
233 
234 @("Floating Point — Infinities")
235 unittest
236 {
237     struct S
238     {
239         real pi;
240         double ni;
241         float i;
242     }
243 
244     S result = parseToml!S(`
245         pi = +inf
246         ni = -inf
247         i = inf
248     `);
249 
250     expect(result.pi).toEqual(real.infinity);
251     expect(result.ni).toEqual(-double.infinity);
252     expect(result.i).toEqual(float.infinity);
253 }
254 
255 @("Floating Point — NaNs")
256 unittest
257 {
258     import std.math : isNaN;
259 
260     struct S
261     {
262         real pNan;
263         double nNan;
264         float nan;
265     }
266 
267     S result = parseToml!S(`
268         pNan = +nan
269         nNan = -nan
270         nan = nan
271     `);
272 
273     assert(result.pNan.isNaN, "Expected result.pNan to be NaN, but got: " ~ result.pNan.to!string);
274     assert(result.nNan.isNaN, "Expected result.nNan to be NaN, but got: " ~ result.nNan.to!string);
275     assert(result.nan.isNaN, "Expected result.nan to be NaN, but got: " ~ result.nan.to!string);
276 }
277 
278 @("Boolean -> bool")
279 unittest
280 {
281     struct S
282     {
283         bool t;
284         bool f;
285     }
286 
287     S result = parseToml!S(`
288         t = true
289         f = false
290     `);
291 
292     expect(result.t).toEqual(true);
293     expect(result.f).toEqual(false);
294 }
295 
296 @("Basic String -> string")
297 unittest
298 {
299     struct S
300     {
301         string s;
302     }
303 
304     S result = parseToml!S(`
305         s = "Appel"
306     `);
307 
308     expect(result.s).toEqual("Appel");
309 }
310 
311 @("Basic Multiline String -> string")
312 unittest
313 {
314     struct S
315     {
316         string s;
317     }
318 
319     S result = parseToml!S(`
320         s = """
321         Phlogiston\tX"""
322     `);
323 
324     expect(result.s).toEqual("        Phlogiston\tX");
325 }
326 
327 @("Literal String -> string")
328 unittest
329 {
330     struct S
331     {
332         string s;
333     }
334 
335     S result = parseToml!S(`
336         s = 'Abc\tde'
337     `);
338 
339     expect(result.s).toEqual("Abc\\tde");
340 }
341 
342 @("Literal Multiline String -> string")
343 unittest
344 {
345     struct S
346     {
347         string s;
348     }
349 
350     S result = parseToml!S(`
351         s = '''
352 Abc\t''de
353 '''
354     `);
355 
356     expect(result.s).toEqual("Abc\\t''de\n");
357 }
358 
359 @("String Unicode test (string, wstring, and dstring)")
360 unittest
361 {
362     struct S
363     {
364         string s;
365         wstring w;
366         dstring d;
367     }
368 
369     S result = parseToml!S(`
370         s = "\U0001F9A2"
371         w = "🐃"
372         d = "🦆"
373     `);
374 
375     expect(result.s).toEqual("🦢");
376     expect(result.w).toEqual("🐃"w);
377     expect(result.d).toEqual("🦆"d);
378 }
379 
380 @("Offset Date-Time -> SysTime")
381 unittest
382 {
383     struct S
384     {
385         SysTime t;
386     }
387 
388     S result = parseToml!S(`
389         t = 2020-01-26 12:09:59Z
390     `);
391 
392     expect(result.t).toEqual(SysTime(
393         DateTime(
394             2020,
395             1,
396             26,
397             12,
398             9,
399             59
400         ),
401         nsecs(0),
402         UTC()
403     ));
404 }
405 
406 @("Local Date-Time -> SysTime where tz = LocalTime")
407 unittest
408 {
409     struct S
410     {
411         SysTime t;
412     }
413 
414     S result = parseToml!S(`
415         t = 2020-01-26 12:09:59
416     `);
417 
418     expect(result.t).toEqual(SysTime(
419         DateTime(
420             2020,
421             1,
422             26,
423             12,
424             9,
425             59
426         ),
427         nsecs(0),
428         null
429     ));
430 }
431 
432 @("Local Date -> Date")
433 unittest
434 {
435     struct S
436     {
437         Date t;
438     }
439 
440     S result = parseToml!S(`
441         t = 2020-01-26
442     `);
443 
444     expect(result.t).toEqual(Date(2020, 1, 26));
445 }
446 
447 @("Local Time -> TimeOfDay")
448 unittest
449 {
450     struct S
451     {
452         TimeOfDay t;
453     }
454 
455     S result = parseToml!S(`
456         t = 12:09:59
457     `);
458 
459     expect(result.t).toEqual(TimeOfDay(12, 9, 59));
460 }
461 
462 @("Array of Integers -> static long[]")
463 unittest
464 {
465     struct S
466     {
467         long[11] t;
468     }
469 
470     S result = parseToml!S(`
471         t = [
472             123,
473             +111,
474             -82,
475             0,
476             +0,
477             -0,
478             525_600,
479             -189_912,
480             0xbEaD_fAcE,
481             0o777,
482             0b11001101
483         ]
484     `);
485 
486     expect(result.t).toEqual([
487         123L,
488         +111L,
489         -82L,
490         0L,
491         +0L,
492         -0L,
493         525_600L,
494         -189_912L,
495         0xbEaD_fAcEL,
496         511L,
497         0b11001101L,
498     ].staticArray!(long, 11));
499 }
500 
501 @("Array of Integers -> static int[]")
502 unittest
503 {
504     struct S
505     {
506         int[10] t;
507     }
508 
509     S result = parseToml!S(`
510         t = [
511             123,
512             +111,
513             -82,
514             0,
515             +0,
516             -0,
517             525_600,
518             -189_912,
519             0o777,
520             0b11001101
521         ]
522     `);
523 
524     expect(result.t).toEqual([
525         123,
526         +111,
527         -82,
528         0,
529         +0,
530         -0,
531         525_600,
532         -189_912,
533         511,
534         0b11001101,
535     ].staticArray!(int, 10));
536 }
537 
538 @("Array of Integers -> static short[]")
539 unittest
540 {
541     struct S
542     {
543         short[10] t;
544     }
545 
546     S result = parseToml!S(`
547         t = [
548             123,
549             +111,
550             -82,
551             0,
552             +0,
553             -0,
554             5_600,
555             -9_912,
556             0o777,
557             0b11001101
558         ]
559     `);
560 
561     expect(result.t).toEqual([
562         123,
563         +111,
564         -82,
565         0,
566         +0,
567         -0,
568         5_600,
569         -9_912,
570         511,
571         0b11001101,
572     ].staticArray!(short, 10));
573 }
574 
575 @("Array of Integers -> static byte[]")
576 unittest
577 {
578     struct S
579     {
580         byte[7] t;
581     }
582 
583     S result = parseToml!S(`
584         t = [
585             1_2_3,
586             +1_1_1,
587             -82,
588             0,
589             +0,
590             -0,
591             0b01001101
592         ]
593     `);
594 
595     expect(result.t).toEqual([
596         123,
597         +111,
598         -82,
599         0,
600         +0,
601         -0,
602         0b01001101,
603     ].staticArray!(byte, 7));
604 }
605 
606 @("Array of Integers -> static ulong[]")
607 unittest
608 {
609     struct S
610     {
611         ulong[9] t;
612     }
613 
614     S result = parseToml!S(`
615         t = [
616             123,
617             +111,
618             0,
619             +0,
620             -0,             # This should still work (both the -0 and this comment).
621             525_600,
622             0xbEaD_fAcE,
623             0o777,
624             0b11001101
625         ]
626     `);
627 
628     expect(result.t).toEqual([
629         123L,
630         +111L,
631         0L,
632         +0L,
633         -0L,
634         525_600L,
635         0xbEaD_fAcEL,
636         511L,
637         0b11001101L,
638     ].staticArray!(ulong, 9));
639 }
640 
641 @("Array of Integers -> static uint[]")
642 unittest
643 {
644     struct S
645     {
646         uint[8] t;
647     }
648 
649     S result = parseToml!S(`
650         t = [
651             123,
652             +111,
653             0,
654             +0,
655             -0,
656             525_600,
657             0o777,
658             0b11001101
659         ]
660     `);
661 
662     expect(result.t).toEqual([
663         123,
664         +111,
665         0,
666         +0,
667         -0,
668         525_600,
669         511,
670         0b11001101,
671     ].staticArray!(uint, 8));
672 }
673 
674 @("Array of Integers -> static ushort[]")
675 unittest
676 {
677     struct S
678     {
679         ushort[8] t;
680     }
681 
682     S result = parseToml!S(`
683         t = [
684             123,
685             +111,
686             0,
687             +0,
688             -0,
689             5_600,
690             0o777,
691             0b11001101
692         ]
693     `);
694 
695     expect(result.t).toEqual([
696         123,
697         +111,
698         0,
699         +0,
700         -0,
701         5_600,
702         511,
703         0b11001101,
704     ].staticArray!(ushort, 8));
705 }
706 
707 @("Array of Integers -> static ubyte[]")
708 unittest
709 {
710     struct S
711     {
712         ubyte[6] t;
713     }
714 
715     S result = parseToml!S(`
716         t = [
717             1_2_3,
718             +1_1_1,
719             0,
720             +0,
721             -0,
722             0b11001101
723         ]
724     `);
725 
726     expect(result.t).toEqual([
727         123,
728         +111,
729         0,
730         +0,
731         -0,
732         0b11001101,
733     ].staticArray!(ubyte, 6));
734 }
735 
736 @("Array of Integers -> dynamic int[]")
737 unittest
738 {
739     struct S
740     {
741         int[] t;
742     }
743 
744     S result = parseToml!S(`
745         t = [
746             123,
747             +111,
748             -82,
749         #   0,
750         #   +0,
751         #   -0,
752         #   525_600,
753         #   -189_912,
754         #   0o777,
755         #   0b11001101
756         ]
757     `);
758 
759     expect(result.t).toEqual([ 123, +111, -82, ]);
760 
761     result = parseToml!S(`
762         t = [
763         #   123,
764         #   +111,
765         #   -82,
766         #   0,
767         #   +0,
768         #   -0,
769         #   525_600,
770             -189_912,
771             0o777,
772             0b11001101
773         ]
774     `);
775 
776     expect(result.t).toEqual([ -189_912, 511, 0b11001101 ]);
777 }
778 
779 @("Array of Strings -> dynamic string[]")
780 unittest
781 {
782     struct S
783     {
784         string[] t;
785     }
786 
787     S result = parseToml!S(`
788         t = [ "do", "re", "mi" ]
789     `);
790 
791     expect(result.t).toEqual([ "do", "re", "mi" ]);
792 }
793 
794 @("Array of Floats -> dynamic float[]")
795 unittest
796 {
797     struct S
798     {
799         float[] t;
800     }
801 
802     S result = parseToml!S(`
803         t = [ 0.0, 2e5, -3.6e-2 ]
804     `);
805 
806     expect(result.t).toEqual([ 0.0f, 2e5f, -3.6e-2f ]);
807 }
808 
809 @("Array of Booleans -> dynamic bool[]")
810 unittest
811 {
812     struct S
813     {
814         bool[] t;
815     }
816 
817     S result = parseToml!S(`
818         t = [ true, false, false, true,
819          ]
820     `);
821 
822     expect(result.t).toEqual([ true, false, false, true ]);
823 }
824 
825 @("Array of Offset Date-Times -> dynamic SysTime[]")
826 unittest
827 {
828     struct S
829     {
830         SysTime[] t;
831     }
832 
833     S result = parseToml!S(`
834         t = [ 2020-02-02 12:51:05Z ]
835     `);
836 
837     expect(result.t).toEqual([ SysTime(DateTime(2020, 2, 2, 12, 51, 5), UTC()) ]);
838 }
839 
840 @("Array of Local Date-Times -> dynamic SysTime[]")
841 unittest
842 {
843     struct S
844     {
845         SysTime[] t;
846     }
847 
848     S result = parseToml!S(`
849         t = [ 2020-02-02 12:51:05 ]
850     `);
851 
852     expect(result.t).toEqual([ SysTime(DateTime(2020, 2, 2, 12, 51, 5), LocalTime()) ]);
853 }
854 
855 @("Array of Local Dates -> dynamic Date[]")
856 unittest
857 {
858     struct S
859     {
860         Date[] t;
861     }
862 
863     S result = parseToml!S(`
864         t = [ 2020-02-02, 2020-10-31 ]
865     `);
866 
867     expect(result.t).toEqual([ Date(2020, 2, 2), Date(2020, 10, 31) ]);
868 }
869 
870 @("Array of Local Times -> dynamic TimeOfDay[]")
871 unittest
872 {
873     struct S
874     {
875         TimeOfDay[] t;
876     }
877 
878     S result = parseToml!S(`
879         t = [ 12:53:23, 00:20:01, 19:22:54 ]
880     `);
881 
882     expect(result.t).toEqual([ TimeOfDay(12, 53, 23), TimeOfDay(0, 20, 1), TimeOfDay(19, 22, 54), ]);
883 }
884 
885 @("Arrays — Empty")
886 unittest
887 {
888     struct S
889     {
890         int[] i;
891         float[] f;
892         string[] s;
893         TimeOfDay[] t;
894     }
895 
896     S s;
897 
898     s = parseToml!S(`
899     i = []
900     f = []
901     s = []
902     t = []
903     `);
904 
905     expect(s.i.length).toEqual(0);
906     expect(s.f.length).toEqual(0);
907     expect(s.s.length).toEqual(0);
908     expect(s.t.length).toEqual(0);
909 }
910 
911 @("Array of Inline Tables -> Array of Structs")
912 unittest
913 {
914     struct S
915     {
916         struct Musician
917         {
918             string name;
919             Date dob;
920         }
921 
922         Musician[3] musicians;
923     }
924 
925     S s = parseToml!S(`
926     musicians = [
927         { name = "Bob Dylan", dob = 1941-05-24 },
928         { name = "Frank Sinatra", dob = 1915-12-12 },
929         { name = "Scott Joplin", dob = 1868-11-24 }
930     ]
931     `);
932 
933     expect(s.musicians[0]).toEqual(S.Musician("Bob Dylan", Date(1941, 5, 24)));
934     expect(s.musicians[1]).toEqual(S.Musician("Frank Sinatra", Date(1915, 12, 12)));
935     expect(s.musicians[2]).toEqual(S.Musician("Scott Joplin", Date(1868, 11, 24)));
936 }
937 
938 @("Table - one level")
939 unittest
940 {
941     struct Inner
942     {
943         int x;
944     }
945 
946     struct Outer
947     {
948         Inner inn;
949         int x;
950     }
951 
952     Outer o = parseToml!Outer(`
953     x = 5
954 
955     [inn]
956     x = 10
957     `);
958 
959     expect(o.x).toEqual(5);
960     expect(o.inn.x).toEqual(10);
961 }
962 
963 @("Table - two levels")
964 unittest
965 {
966     struct Nucleus
967     {
968         int c;
969     }
970 
971     struct Inner
972     {
973         Nucleus nuc;
974     }
975 
976     struct Outer
977     {
978         Inner inn;
979     }
980 
981     Outer o = parseToml!Outer(`
982     [inn.nuc]
983     c = 2
984     `);
985 
986     expect(o.inn.nuc.c).toEqual(2);
987 }
988 
989 @("Table - three level + unicode")
990 unittest
991 {
992     struct InnerCore
993     {
994         int heat;
995     }
996 
997     struct OuterCore
998     {
999         InnerCore iñner;
1000     }
1001 
1002     struct Mantle
1003     {
1004         OuterCore outer;
1005     }
1006 
1007     struct Earth
1008     {
1009         Mantle mantle;
1010     }
1011 
1012     Earth earth = parseToml!Earth(`
1013 
1014     [mantle.outer."iñner"]
1015     heat = 9001
1016 
1017     `);
1018 
1019     expect(earth.mantle.outer.iñner.heat).toEqual(9001);
1020 }
1021 
1022 @("Table - dotted keys")
1023 unittest
1024 {
1025     struct InnerCore
1026     {
1027         int heat;
1028     }
1029 
1030     struct OuterCore
1031     {
1032         InnerCore iñner;
1033     }
1034 
1035     struct Mantle
1036     {
1037         OuterCore outer;
1038     }
1039 
1040     struct Earth
1041     {
1042         Mantle mantle;
1043     }
1044 
1045     Earth earth = parseToml!Earth(`
1046 
1047     [mantle.outer]
1048     "iñner".heat = 9001
1049 
1050     `);
1051 
1052     expect(earth.mantle.outer.iñner.heat).toEqual(9001);
1053 }
1054 
1055 @("Table - inline")
1056 unittest
1057 {
1058     struct InnerCore
1059     {
1060         int heat;
1061     }
1062 
1063     struct OuterCore
1064     {
1065         InnerCore iñner;
1066     }
1067 
1068     struct Mantle
1069     {
1070         OuterCore outer;
1071     }
1072 
1073     struct Earth
1074     {
1075         Mantle mantle;
1076     }
1077 
1078     Earth earth = parseToml!Earth(`
1079     mantle = { outer = { "iñner" = { heat = 9001 } } }
1080     `);
1081 
1082     expect(earth.mantle.outer.iñner.heat).toEqual(9001);
1083 }
1084 
1085 @("Table Array")
1086 unittest
1087 {
1088     struct S
1089     {
1090         struct Musician
1091         {
1092             string name;
1093             Date dob;
1094         }
1095 
1096         Musician[3] musicians;
1097     }
1098 
1099     S s = parseToml!S(`
1100     [[musicians]]
1101     name = "Bob Dylan"
1102     dob = 1941-05-24
1103 
1104     [[musicians]]
1105     name = "Frank Sinatra"
1106     dob = 1915-12-12
1107 
1108     [[musicians]]
1109     name = "Scott Joplin"
1110     dob = 1868-11-24
1111     `);
1112 
1113     expect(s.musicians[0]).toEqual(S.Musician("Bob Dylan", Date(1941, 5, 24)));
1114     expect(s.musicians[1]).toEqual(S.Musician("Frank Sinatra", Date(1915, 12, 12)));
1115     expect(s.musicians[2]).toEqual(S.Musician("Scott Joplin", Date(1868, 11, 24)));
1116 }
1117 
1118 @("Table Array — dotted keys")
1119 unittest
1120 {
1121     struct Country
1122     {
1123         string nameEnglish;
1124         string nameLocal;
1125     }
1126 
1127     struct S
1128     {
1129         struct Countries
1130         {
1131             Country[2] republics;
1132             Country[2] monarchies;
1133 
1134             size_t count;
1135         }
1136 
1137         Countries countries;
1138     }
1139 
1140     S s = parseToml!S(`
1141 
1142     countries.count = 4
1143 
1144     [[countries.republics]]
1145     nameEnglish = "Ireland"
1146     nameLocal = "Éire"
1147 
1148     [[countries.republics]]
1149     nameEnglish = "Greece"
1150     nameLocal = "Ελλάδα"
1151 
1152     [[countries.monarchies]]
1153     nameEnglish = "Bhutan"
1154     nameLocal = "འབྲུག་ཡུལ་"
1155 
1156     [[countries.monarchies]]
1157     nameEnglish = "Denmark"
1158     nameLocal = "Danmark"
1159 
1160     `);
1161 
1162     expect(s.countries.count).toEqual(4);
1163     expect(s.countries.republics[0]).toEqual(Country("Ireland", "Éire"));
1164     expect(s.countries.republics[1]).toEqual(Country("Greece", "Ελλάδα"));
1165     expect(s.countries.monarchies[0]).toEqual(Country("Bhutan", "འབྲུག་ཡུལ་"));
1166     expect(s.countries.monarchies[1]).toEqual(Country("Denmark", "Danmark"));
1167 }
1168 
1169 @("Table Array — empty entry")
1170 unittest
1171 {
1172     struct S
1173     {
1174         struct Musician
1175         {
1176             string name;
1177             Date dob;
1178         }
1179 
1180         Musician[3] musicians;
1181     }
1182 
1183     S s = parseToml!S(`
1184     [[musicians]]
1185     name = "Bob Dylan"
1186     dob = 1941-05-24
1187 
1188     [[musicians]]
1189 
1190     [[musicians]]
1191     name = "Scott Joplin"
1192     dob = 1868-11-24
1193     `);
1194 
1195     expect(s.musicians[0]).toEqual(S.Musician("Bob Dylan", Date(1941, 5, 24)));
1196     expect(s.musicians[1]).toEqual(S.Musician());
1197     expect(s.musicians[2]).toEqual(S.Musician("Scott Joplin", Date(1868, 11, 24)));
1198 }
1199 
1200 @("Integer can't fit into a byte")
1201 unittest
1202 {
1203     struct S
1204     {
1205         byte i;
1206     }
1207 
1208     expect({ parseToml!S(`i = 128`); }).toThrow!TomlDecodingException;
1209 }
1210 
1211 @("Integer can't fit into a ubyte")
1212 unittest
1213 {
1214     struct S
1215     {
1216         byte i;
1217     }
1218 
1219     expect({ parseToml!S(`i = 256`); }).toThrow!TomlDecodingException;
1220 }
1221 
1222 @("Integer can't fit into a short")
1223 unittest
1224 {
1225     struct S
1226     {
1227         short i;
1228     }
1229 
1230     expect({ parseToml!S(`i = 32768`); }).toThrow!TomlDecodingException;
1231 }
1232 
1233 @("Integer can't fit into a ushort")
1234 unittest
1235 {
1236     struct S
1237     {
1238         ushort i;
1239     }
1240 
1241     expect({ parseToml!S(`i = 65536`); }).toThrow!TomlDecodingException;
1242 }
1243 
1244 @("Integer too small (ushort)")
1245 unittest
1246 {
1247     struct S
1248     {
1249         ushort i;
1250     }
1251 
1252     expect({ parseToml!S(`i = -1`); }).toThrow!TomlDecodingException;
1253 }
1254 
1255 @("Integer too small (short)")
1256 unittest
1257 {
1258     struct S
1259     {
1260         ushort i;
1261     }
1262 
1263     expect({ parseToml!S(`i = -32769`); }).toThrow!TomlDecodingException;
1264 }
1265 
1266 @("Offset date-time before year 0")
1267 unittest
1268 {
1269     struct S
1270     {
1271         SysTime timestamp;
1272     }
1273 
1274     expect({ parseToml!S(`timestamp = -0001-01-01 00:00:00Z`); }).toThrow!TomlDecodingException;
1275 }
1276 
1277 @("Offset date-time after year 9999")
1278 unittest
1279 {
1280     struct S
1281     {
1282         SysTime timestamp;
1283     }
1284 
1285     expect({ parseToml!S(`timestamp = 10000-01-01 00:00:00Z`); }).toThrow!TomlDecodingException;
1286 }
1287 
1288 @("Date before year 0")
1289 unittest
1290 {
1291     struct S
1292     {
1293         SysTime date;
1294     }
1295 
1296     expect({ parseToml!S(`date = -0001-01-01`); }).toThrow!TomlDecodingException;
1297 }
1298 
1299 @("Date after year 9999")
1300 unittest
1301 {
1302     struct S
1303     {
1304         Date date;
1305     }
1306 
1307     expect({ parseToml!S(`date = 10000-01-01`); }).toThrow!TomlDecodingException;
1308 }
1309 
1310 @("Invalid month 0")
1311 unittest
1312 {
1313     struct S
1314     {
1315         Date date;
1316     }
1317 
1318     expect({ parseToml!S(`date = 2020-00-01`); }).toThrow!TomlDecodingException;
1319 }
1320 
1321 @("Invalid month 13")
1322 unittest
1323 {
1324     struct S
1325     {
1326         Date date;
1327     }
1328 
1329     expect({ parseToml!S(`date = 2020-13-01`); }).toThrow!TomlDecodingException;
1330 }
1331 
1332 @("Invalid day of month")
1333 unittest
1334 {
1335     struct S
1336     {
1337         Date date;
1338     }
1339 
1340     expect({ parseToml!S(`date = 2011-02-29`); }).toThrow!TomlDecodingException;
1341 }
1342 
1343 @("Invalid time")
1344 unittest
1345 {
1346     struct S
1347     {
1348         TimeOfDay time;
1349     }
1350 
1351     expect({ parseToml!S(`time = 22:61:00`); }).toThrow!TomlDecodingException;
1352     expect({ parseToml!S(`time = 24:01:00`); }).toThrow!TomlDecodingException;
1353     expect({ parseToml!S(`time = 22:01:60`); }).toThrow!TomlDecodingException;
1354 }
1355 
1356 @("Overly precise floating point")
1357 unittest
1358 {
1359     struct S
1360     {
1361         float f;
1362     }
1363 
1364     expect({ parseToml!S(`f = 3.1415926535897932384626`); }).not.toThrow!Exception;
1365 }
1366 
1367 @("Integer outside of [long.max, long.min]")
1368 unittest
1369 {
1370     struct S
1371     {
1372         ulong x;
1373     }
1374 
1375     expect({ parseToml!S(`x = 18446744073709551615`); }).toThrow!TomlDecodingException;
1376     expect({ parseToml!S(`x = -18446744073709551615`); }).toThrow!TomlDecodingException;
1377 }
1378 
1379 @("Integer keys as array indices")
1380 unittest
1381 {
1382     struct S
1383     {
1384         int[5] x;
1385     }
1386 
1387     expect(parseToml!S(`
1388         [x]
1389         0 = 11
1390         1 = 22
1391         2 = 33
1392         3 = 44
1393         4 = 55
1394     `)).toEqual(S([11, 22, 33, 44, 55]));
1395 }
1396 
1397 @("Integer keys as named fields")
1398 unittest
1399 {
1400     struct S
1401     {
1402         struct X
1403         {
1404             @TomlName("1") int a;
1405             @TomlName("2") int b;
1406             @TomlName("3") int c;
1407             @TomlName("4") int d;
1408             @TomlName("5") int e;
1409         }
1410 
1411         X x;
1412     }
1413 
1414     expect(parseToml!S(`
1415         [x]
1416         1 = 11
1417         2 = 22
1418         3 = 33
1419         4 = 44
1420         5 = 55
1421     `)).toEqual(S(S.X(11, 22, 33, 44, 55)));
1422 }
1423 
1424 @("Non-existent keys should be ignored")
1425 unittest
1426 {
1427     struct S
1428     {
1429         int a;
1430     }
1431 
1432     expect(parseToml!S(`
1433         a = 5
1434         b = 6
1435     `)).toEqual(S(5));
1436 }
1437 
1438 @("Non-existent tables should be ignored")
1439 unittest
1440 {
1441     struct S
1442     {
1443         int a;
1444     }
1445 
1446     expect(parseToml!S(`
1447         a = 5
1448 
1449         [x]
1450         b = 6
1451     `)).toEqual(S(5));
1452 }
1453 
1454 @("A key corresponding to a non-struct property should work")
1455 unittest
1456 {
1457     struct S
1458     {
1459         private int _a;
1460         int a() @property const { return _a; }
1461         void a(int newA) @property { _a = newA; }
1462     }
1463 
1464     S s;
1465 
1466     parseToml!S(`
1467         a = 5
1468     `, s);
1469 
1470     expect(s).toEqual(S(5));
1471 }
1472 
1473 @("A key corresponding to a public property of a struct type should not compile")
1474 unittest
1475 {
1476     struct S
1477     {
1478         private struct Inner
1479         {
1480             int x;
1481         }
1482 
1483         private Inner _inner;
1484         Inner inner() @property const { return _inner; }
1485         void inner(Inner newInner) @property { _inner = newInner; }
1486     }
1487 
1488     S s;
1489 
1490     static assert(
1491         !__traits(compiles, parseToml!S(`
1492             blah
1493         `, s)),
1494         "Expected compilation to fail when struct contains public property that is itself a struct."
1495     );
1496 }
1497 
1498 @("If TOML array length exceeds static array length, ignore additional entries.")
1499 unittest
1500 {
1501     struct S
1502     {
1503         int[5] arr;
1504     }
1505 
1506     expect(parseToml!S(`
1507         arr = [11, 22, 33, 44, 55, 66, 77, 88]
1508     `)).toEqual(S([11, 22, 33, 44, 55]));
1509 }
1510 
1511 @("Don't assign non-public fields")
1512 unittest
1513 {
1514     struct S
1515     {
1516         private int x;
1517     }
1518 
1519     expect(parseToml!S(`x = 3`)).toEqual(S());
1520 }