1#[doc(hidden)]
222pub use toml_example_derive::TomlExample;
223pub mod traits;
224pub use traits::*;
225
226#[cfg(test)]
227mod tests {
228 use crate as toml_example;
229 use serde_derive::Deserialize;
230 use std::collections::HashMap;
231 use toml_example::TomlExample;
232
233 #[test]
234 fn basic() {
235 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
236 #[allow(dead_code)]
237 struct Config {
238 a: usize,
240 b: String,
242 }
243 assert_eq!(
244 Config::toml_example(),
245 r#"# Config.a should be a number
246a = 0
247
248# Config.b should be a string
249b = ""
250
251"#
252 );
253 assert_eq!(
254 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
255 Config::default()
256 );
257 let mut tmp_file = std::env::temp_dir();
258 tmp_file.push("config.toml");
259 Config::to_toml_example(&tmp_file.as_path().to_str().unwrap()).unwrap();
260 assert_eq!(
261 std::fs::read_to_string(tmp_file).unwrap(),
262 r#"# Config.a should be a number
263a = 0
264
265# Config.b should be a string
266b = ""
267
268"#
269 );
270 }
271
272 #[test]
273 fn option() {
274 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
275 #[allow(dead_code)]
276 struct Config {
277 a: Option<usize>,
279 b: Option<String>,
281 }
282 assert_eq!(
283 Config::toml_example(),
284 r#"# Config.a is an optional number
285# a = 0
286
287# Config.b is an optional string
288# b = ""
289
290"#
291 );
292 assert_eq!(
293 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
294 Config::default()
295 )
296 }
297
298 #[test]
299 fn vec() {
300 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
301 #[allow(dead_code)]
302 struct Config {
303 a: Vec<usize>,
305 b: Vec<String>,
307 c: Vec<Option<usize>>,
309 d: Option<Vec<usize>>,
311 }
312 assert_eq!(
313 Config::toml_example(),
314 r#"# Config.a is a list of number
315a = [ 0, ]
316
317# Config.b is a list of string
318b = [ "", ]
319
320# Config.c
321c = [ 0, ]
322
323# Config.d
324# d = [ 0, ]
325
326"#
327 );
328 assert!(toml::from_str::<Config>(&Config::toml_example()).is_ok())
329 }
330
331 #[test]
332 fn struct_doc() {
333 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
336 #[allow(dead_code)]
337 struct Config {
338 a: usize,
341 }
342 assert_eq!(
343 Config::toml_example(),
344 r#"# Config is to arrange something or change the controls on a computer or other device
345# so that it can be used in a particular way
346# Config.a should be a number
347# the number should be greater or equal zero
348a = 0
349
350"#
351 );
352 assert_eq!(
353 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
354 Config::default()
355 )
356 }
357
358 #[test]
359 fn serde_default() {
360 fn default_a() -> usize {
361 7
362 }
363 fn default_b() -> String {
364 "default".into()
365 }
366 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
367 #[allow(dead_code)]
368 struct Config {
369 #[serde(default = "default_a")]
371 a: usize,
372 #[serde(default = "default_b")]
374 b: String,
375 #[serde(default)]
377 c: usize,
378 #[serde(default)]
380 d: String,
381 #[serde(default)]
382 e: Option<usize>,
383 }
384 assert_eq!(
385 Config::toml_example(),
386 r#"# Config.a should be a number
387a = 7
388
389# Config.b should be a string
390b = "default"
391
392# Config.c should be a number
393c = 0
394
395# Config.d should be a string
396d = ""
397
398# e = 0
399
400"#
401 );
402 }
403
404 #[test]
405 fn toml_example_default() {
406 fn default_str() -> String {
407 "seven".into()
408 }
409 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
410 #[allow(dead_code)]
411 struct Config {
412 #[toml_example(default = 7)]
414 a: usize,
415 #[toml_example(default = "default")]
417 #[serde(default = "default_str")]
418 b: String,
419 #[serde(default = "default_str")]
420 #[toml_example(default = "default")]
421 c: String,
422 #[toml_example(default = [ "default", ])]
423 e: Vec<String>,
424 #[toml_example(
425 default = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
426 )]
427 f: String,
428 #[toml_example(default = [ "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
429 "second",
430 "third",
431 ])]
432 g: Vec<String>,
433 #[toml_example(default = "#FAFAFA")]
435 color: String,
436 }
437 assert_eq!(
438 Config::toml_example(),
439 r##"# Config.a should be a number
440a = 7
441
442# Config.b should be a string
443b = "seven"
444
445c = "default"
446
447e = ["default",]
448
449f = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
450
451g = ["super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
452"second", "third",]
453
454# Config.color should be a hex color code
455color = "#FAFAFA"
456
457"##
458 );
459 }
460
461 #[test]
462 fn struct_serde_default() {
463 #[derive(TomlExample, Deserialize, PartialEq)]
464 #[serde(default)]
465 struct Foo {
466 bar: String,
467 #[serde(default)]
468 x: usize,
469 }
470 impl Default for Foo {
471 fn default() -> Self {
472 Foo {
473 bar: String::from("hello world"),
474 x: 12,
475 }
476 }
477 }
478 assert_eq!(
479 Foo::toml_example(),
480 r##"bar = "hello world"
481
482x = 0
483
484"##
485 );
486 }
487
488 #[test]
489 fn struct_serde_default_fn() {
490 #[derive(TomlExample, Deserialize, PartialEq)]
491 #[serde(default = "default")]
492 struct Foo {
493 bar: String,
494 #[toml_example(default = "field override")]
495 baz: String,
496 }
497 fn default() -> Foo {
498 Foo {
499 bar: String::from("hello world"),
500 baz: String::from("custom default"),
501 }
502 }
503 assert_eq!(
504 Foo::toml_example(),
505 r##"bar = "hello world"
506
507baz = "field override"
508
509"##
510 );
511 }
512
513 #[test]
514 fn struct_toml_example_default() {
515 #[derive(TomlExample, Deserialize, PartialEq)]
516 #[toml_example(default)]
517 struct Foo {
518 #[serde(default = "paru")]
519 yay: &'static str,
520 aur_is_useful: bool,
521 }
522 impl Default for Foo {
523 fn default() -> Self {
524 Foo {
525 yay: "yay!",
526 aur_is_useful: true,
527 }
528 }
529 }
530 fn paru() -> &'static str {
531 "no, paru!"
532 }
533 assert_eq!(
534 Foo::toml_example(),
535 r##"yay = "no, paru!"
536
537aur_is_useful = true
538
539"##
540 );
541 }
542
543 #[test]
544 fn no_nesting() {
545 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
547 #[allow(dead_code)]
548 struct Inner {
549 a: usize,
551 }
552 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
553 #[allow(dead_code)]
554 struct Outer {
555 inner: Inner,
557 }
558 assert_eq!(
559 Outer::toml_example(),
560 r#"# Outer.inner is a complex struct
561inner = ""
562
563"#
564 );
565 }
566
567 #[test]
568 fn nesting() {
569 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
571 #[allow(dead_code)]
572 struct Inner {
573 a: usize,
575 }
576 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
577 #[allow(dead_code)]
578 struct Outer {
579 #[toml_example(nesting)]
581 inner: Inner,
582 }
583 assert_eq!(
584 Outer::toml_example(),
585 r#"# Outer.inner is a complex struct
586# Inner is a config live in Outer
587[inner]
588# Inner.a should be a number
589a = 0
590
591"#
592 );
593 assert_eq!(
594 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
595 Outer::default()
596 );
597 }
598
599 #[test]
600 fn nesting_by_section() {
601 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
603 #[allow(dead_code)]
604 struct Inner {
605 a: usize,
607 }
608 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
609 #[allow(dead_code)]
610 struct Outer {
611 #[toml_example(nesting = section)]
613 inner: Inner,
614 }
615 assert_eq!(
616 Outer::toml_example(),
617 r#"# Outer.inner is a complex struct
618# Inner is a config live in Outer
619[inner]
620# Inner.a should be a number
621a = 0
622
623"#
624 );
625 assert_eq!(
626 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
627 Outer::default()
628 );
629 }
630
631 #[test]
632 fn nesting_by_prefix() {
633 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
635 #[allow(dead_code)]
636 struct Inner {
637 a: usize,
639 }
640 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
641 #[allow(dead_code)]
642 struct Outer {
643 #[toml_example(nesting = prefix)]
645 inner: Inner,
646 }
647 assert_eq!(
648 Outer::toml_example(),
649 r#"# Outer.inner is a complex struct
650# Inner is a config live in Outer
651# Inner.a should be a number
652inner.a = 0
653
654"#
655 );
656 assert_eq!(
657 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
658 Outer::default()
659 );
660 }
661
662 #[test]
663 fn nesting_vector() {
664 #[derive(TomlExample, Deserialize)]
666 #[allow(dead_code)]
667 struct Service {
668 port: usize,
670 }
671 #[derive(TomlExample, Deserialize)]
672 #[allow(dead_code)]
673 struct Node {
674 #[toml_example(nesting)]
676 services: Vec<Service>,
677 }
678 assert_eq!(
679 Node::toml_example(),
680 r#"# Services are running in the node
681# Service with specific port
682[[services]]
683# port should be a number
684port = 0
685
686"#
687 );
688 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
689 }
690
691 #[test]
692 fn nesting_hashmap() {
693 #[derive(TomlExample, Deserialize)]
695 #[allow(dead_code)]
696 struct Service {
697 port: usize,
699 }
700 #[derive(TomlExample, Deserialize)]
701 #[allow(dead_code)]
702 struct Node {
703 #[toml_example(nesting)]
705 services: HashMap<String, Service>,
706 }
707 assert_eq!(
708 Node::toml_example(),
709 r#"# Services are running in the node
710# Service with specific port
711[services.example]
712# port should be a number
713port = 0
714
715"#
716 );
717 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
718 }
719
720 #[test]
721 fn optional_nesting() {
722 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
724 #[allow(dead_code)]
725 struct Inner {
726 a: usize,
728 }
729 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
730 #[allow(dead_code)]
731 struct Outer {
732 #[toml_example(nesting)]
734 inner: Option<Inner>,
735 }
736 assert_eq!(
737 Outer::toml_example(),
738 r#"# Outer.inner is a complex struct
739# Inner is a config live in Outer
740# [inner]
741# Inner.a should be a number
742# a = 0
743
744"#
745 );
746 assert_eq!(
747 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
748 Outer::default()
749 );
750 }
751
752 #[test]
753 fn optional_nesting_by_section() {
754 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
756 #[allow(dead_code)]
757 struct Inner {
758 a: usize,
760 }
761 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
762 #[allow(dead_code)]
763 struct Outer {
764 #[toml_example(nesting = section)]
766 inner: Option<Inner>,
767 }
768 assert_eq!(
769 Outer::toml_example(),
770 r#"# Outer.inner is a complex struct
771# Inner is a config live in Outer
772# [inner]
773# Inner.a should be a number
774# a = 0
775
776"#
777 );
778 assert_eq!(
779 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
780 Outer::default()
781 );
782 }
783
784 #[test]
785 fn optional_nesting_by_prefix() {
786 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
788 #[allow(dead_code)]
789 struct Inner {
790 a: usize,
792 }
793 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
794 #[allow(dead_code)]
795 struct Outer {
796 #[toml_example(nesting = prefix)]
798 inner: Option<Inner>,
799 }
800 assert_eq!(
801 Outer::toml_example(),
802 r#"# Outer.inner is a complex struct
803# Inner is a config live in Outer
804# Inner.a should be a number
805# inner.a = 0
806
807"#
808 );
809 assert_eq!(
810 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
811 Outer::default()
812 );
813 }
814
815 #[test]
816 fn optional_nesting_vector() {
817 #[derive(TomlExample, Deserialize)]
819 #[allow(dead_code)]
820 struct Service {
821 port: usize,
823 }
824 #[derive(TomlExample, Deserialize)]
825 #[allow(dead_code)]
826 struct Node {
827 #[toml_example(nesting)]
829 services: Option<Vec<Service>>,
830 }
831 assert_eq!(
832 Node::toml_example(),
833 r#"# Services are running in the node
834# Service with specific port
835# [[services]]
836# port should be a number
837# port = 0
838
839"#
840 );
841 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
842 }
843
844 #[test]
845 fn optional_nesting_hashmap() {
846 #[derive(TomlExample, Deserialize)]
848 #[allow(dead_code)]
849 struct Service {
850 port: usize,
852 }
853 #[derive(TomlExample, Deserialize)]
854 #[allow(dead_code)]
855 struct Node {
856 #[toml_example(nesting)]
858 services: Option<HashMap<String, Service>>,
859 }
860 assert_eq!(
861 Node::toml_example(),
862 r#"# Services are running in the node
863# Service with specific port
864# [services.example]
865# port should be a number
866# port = 0
867
868"#
869 );
870 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
871 }
872
873 #[test]
874 fn nesting_hashmap_with_default_name() {
875 #[derive(TomlExample, Deserialize)]
877 #[allow(dead_code)]
878 struct Service {
879 #[toml_example(default = 80)]
881 port: usize,
882 }
883 #[derive(TomlExample, Deserialize)]
884 #[allow(dead_code)]
885 struct Node {
886 #[toml_example(nesting)]
888 #[toml_example(default = http)]
889 services: HashMap<String, Service>,
890 }
891 assert_eq!(
892 Node::toml_example(),
893 r#"# Services are running in the node
894# Service with specific port
895[services.http]
896# port should be a number
897port = 80
898
899"#
900 );
901 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
902 }
903
904 #[test]
905 fn nesting_hashmap_with_dash_name() {
906 #[derive(TomlExample, Deserialize)]
908 #[allow(dead_code)]
909 struct Service {
910 #[toml_example(default = 80)]
912 port: usize,
913 }
914 #[derive(TomlExample, Deserialize)]
915 #[allow(dead_code)]
916 struct Node {
917 #[toml_example(nesting)]
919 #[toml_example(default = http.01)]
920 services: HashMap<String, Service>,
921 }
922 assert_eq!(
923 Node::toml_example(),
924 r#"# Services are running in the node
925# Service with specific port
926[services.http-01]
927# port should be a number
928port = 80
929
930"#
931 );
932 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
933 }
934
935 #[test]
936 fn require() {
937 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
938 #[allow(dead_code)]
939 struct Config {
940 #[toml_example(require)]
942 a: Option<usize>,
943 #[toml_example(require)]
945 b: Option<String>,
946 #[toml_example(require)]
947 #[toml_example(default = "third")]
948 c: Option<String>,
949 }
950 assert_eq!(
951 Config::toml_example(),
952 r#"# Config.a is an optional number
953a = 0
954
955# Config.b is an optional string
956b = ""
957
958c = "third"
959
960"#
961 );
962 }
963
964 #[test]
965 fn skip() {
966 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
967 #[allow(dead_code)]
968 struct Config {
969 a: usize,
971 #[toml_example(skip)]
972 b: usize,
973 #[serde(skip)]
974 c: usize,
975 #[serde(skip_deserializing)]
976 d: usize,
977 }
978 assert_eq!(
979 Config::toml_example(),
980 r#"# Config.a is a number
981a = 0
982
983"#
984 );
985 }
986
987 #[test]
988 fn is_enum() {
989 fn b() -> AB {
990 AB::B
991 }
992 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
993 #[allow(dead_code)]
994 struct Config {
995 #[toml_example(enum, default)]
997 ab: AB,
998 #[toml_example(is_enum)]
1000 #[serde(default)]
1001 ab2: AB,
1002 #[toml_example(is_enum)]
1004 #[serde(default = "b")]
1005 ab3: AB,
1006 }
1007 #[derive(Debug, Default, Deserialize, PartialEq)]
1008 enum AB {
1009 #[default]
1010 A,
1011 B,
1012 }
1013 assert_eq!(
1014 Config::toml_example(),
1015 r#"# Config.ab is an enum
1016ab = "A"
1017
1018# Config.ab2 is an enum too
1019ab2 = "A"
1020
1021# Config.ab3 is an enum as well
1022ab3 = "B"
1023
1024"#
1025 );
1026 }
1027
1028 #[test]
1029 fn flatten() {
1030 #[derive(TomlExample)]
1031 struct ItemWrapper {
1032 #[toml_example(flatten, nesting)]
1033 _item: Item,
1034 }
1035 #[derive(TomlExample)]
1036 struct Item {
1037 _value: String,
1038 }
1039 assert_eq!(ItemWrapper::toml_example(), Item::toml_example());
1040 }
1041
1042 #[test]
1043 fn multi_attr_escaping() {
1044 #[derive(TomlExample, Deserialize, PartialEq)]
1045 struct ConfigWrapper {
1046 #[toml_example(default = ["hello", "{nice :)\""], require)]
1047 vec: Option<Vec<String>>,
1048
1049 #[toml_example(require, default = ["\"\\\n}])", "super (fancy\\! :-) )"])]
1050 list: Option<[String; 2]>,
1051 }
1052
1053 assert_eq!(
1054 ConfigWrapper::toml_example(),
1055 r#"vec = ["hello", "{nice :)\""]
1056
1057list = ["\"\\\n}])", "super (fancy\\! :-) )"]
1058
1059"#
1060 );
1061 }
1062
1063 #[test]
1064 fn r_sharp_field() {
1065 #[derive(TomlExample)]
1066 #[allow(dead_code)]
1067 struct Config {
1068 r#type: usize,
1070 }
1071 assert_eq!(
1072 Config::toml_example(),
1073 r#"# Config.type is a number
1074type = 0
1075
1076"#
1077 );
1078 }
1079
1080 #[test]
1081 fn non_nesting_field_should_be_first() {
1082 #[derive(TomlExample)]
1083 #[allow(dead_code)]
1084 struct Foo {
1085 a: String,
1086 }
1087
1088 #[derive(TomlExample)]
1089 #[allow(dead_code)]
1090 struct Bar {
1091 #[toml_example(nesting)]
1092 foo: Foo,
1093 b: String,
1094 }
1095
1096 assert_eq!(
1097 Bar::toml_example(),
1098 r#"b = ""
1099
1100[foo]
1101a = ""
1102
1103"#
1104 );
1105 }
1106
1107 #[test]
1108 fn rename() {
1109 use serde::Serialize;
1110
1111 #[derive(Deserialize, Serialize, TomlExample)]
1112 struct Config {
1113 #[serde(rename = "bb")]
1114 b: usize,
1115 }
1116 assert_eq!(
1117 Config::toml_example(),
1118 r#"bb = 0
1119
1120"#
1121 );
1122 }
1123
1124 #[test]
1125 fn rename_all() {
1126 use serde::Serialize;
1127
1128 #[derive(Deserialize, Serialize, TomlExample)]
1129 #[serde(rename_all = "kebab-case")]
1130 struct Config {
1131 a_a: usize,
1132 }
1133 assert_eq!(
1134 Config::toml_example(),
1135 r#"a-a = 0
1136
1137"#
1138 );
1139 }
1140
1141 #[test]
1142 fn hashset_and_struct() {
1143 use std::collections::HashMap;
1144
1145 #[derive(TomlExample)]
1146 #[allow(dead_code)]
1147 struct Foo {
1148 a: String,
1149 }
1150
1151 #[derive(TomlExample)]
1152 #[allow(dead_code)]
1153 struct Bar {
1154 #[toml_example(nesting)]
1156 default: Foo,
1157
1158 #[toml_example(nesting)]
1160 instance: HashMap<String, Foo>,
1161 }
1162
1163 assert_eq!(
1164 Bar::toml_example(),
1165 r#"# Default instances doc
1166[default]
1167a = ""
1168
1169# Instances doc
1170[instance.example]
1171a = ""
1172
1173"#
1174 );
1175 }
1176}