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