1#[doc(hidden)]
150pub use toml_example_derive::TomlExample;
151pub mod traits;
152pub use traits::*;
153
154#[cfg(test)]
155mod tests {
156 use crate as toml_example;
157 use serde_derive::Deserialize;
158 use std::collections::HashMap;
159 use toml_example::TomlExample;
160
161 #[test]
162 fn basic() {
163 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
164 #[allow(dead_code)]
165 struct Config {
166 a: usize,
168 b: String,
170 }
171 assert_eq!(
172 Config::toml_example(),
173 r#"# Config.a should be a number
174a = 0
175
176# Config.b should be a string
177b = ""
178
179"#
180 );
181 assert_eq!(
182 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
183 Config::default()
184 );
185 let mut tmp_file = std::env::temp_dir();
186 tmp_file.push("config.toml");
187 Config::to_toml_example(&tmp_file.as_path().to_str().unwrap()).unwrap();
188 assert_eq!(
189 std::fs::read_to_string(tmp_file).unwrap(),
190 r#"# Config.a should be a number
191a = 0
192
193# Config.b should be a string
194b = ""
195
196"#
197 );
198 }
199
200 #[test]
201 fn option() {
202 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
203 #[allow(dead_code)]
204 struct Config {
205 a: Option<usize>,
207 b: Option<String>,
209 }
210 assert_eq!(
211 Config::toml_example(),
212 r#"# Config.a is an optional number
213# a = 0
214
215# Config.b is an optional string
216# b = ""
217
218"#
219 );
220 assert_eq!(
221 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
222 Config::default()
223 )
224 }
225
226 #[test]
227 fn vec() {
228 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
229 #[allow(dead_code)]
230 struct Config {
231 a: Vec<usize>,
233 b: Vec<String>,
235 c: Vec<Option<usize>>,
237 d: Option<Vec<usize>>,
239 }
240 assert_eq!(
241 Config::toml_example(),
242 r#"# Config.a is a list of number
243a = [ 0, ]
244
245# Config.b is a list of string
246b = [ "", ]
247
248# Config.c
249c = [ 0, ]
250
251# Config.d
252# d = [ 0, ]
253
254"#
255 );
256 assert!(toml::from_str::<Config>(&Config::toml_example()).is_ok())
257 }
258
259 #[test]
260 fn struct_doc() {
261 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
264 #[allow(dead_code)]
265 struct Config {
266 a: usize,
269 }
270 assert_eq!(
271 Config::toml_example(),
272 r#"# Config is to arrange something or change the controls on a computer or other device
273# so that it can be used in a particular way
274# Config.a should be a number
275# the number should be greater or equal zero
276a = 0
277
278"#
279 );
280 assert_eq!(
281 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
282 Config::default()
283 )
284 }
285
286 #[test]
287 fn serde_default() {
288 fn default_a() -> usize {
289 7
290 }
291 fn default_b() -> String {
292 "default".into()
293 }
294 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
295 #[allow(dead_code)]
296 struct Config {
297 #[serde(default = "default_a")]
299 a: usize,
300 #[serde(default = "default_b")]
302 b: String,
303 #[serde(default)]
305 c: usize,
306 #[serde(default)]
308 d: String,
309 #[serde(default)]
310 e: Option<usize>,
311 }
312 assert_eq!(
313 Config::toml_example(),
314 r#"# Config.a should be a number
315a = 7
316
317# Config.b should be a string
318b = "default"
319
320# Config.c should be a number
321c = 0
322
323# Config.d should be a string
324d = ""
325
326# e = 0
327
328"#
329 );
330 }
331
332 #[test]
333 fn toml_example_default() {
334 fn default_str() -> String {
335 "seven".into()
336 }
337 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
338 #[allow(dead_code)]
339 struct Config {
340 #[toml_example(default = 7)]
342 a: usize,
343 #[toml_example(default = "default")]
345 #[serde(default = "default_str")]
346 b: String,
347 #[serde(default = "default_str")]
348 #[toml_example(default = "default")]
349 c: String,
350 #[toml_example(default = [ "default", ])]
351 e: Vec<String>,
352 #[toml_example(
353 default = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
354 )]
355 f: String,
356 #[toml_example(default = [ "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
357 "second",
358 "third",
359 ])]
360 g: Vec<String>,
361 #[toml_example(default = "#FAFAFA")]
363 color: String,
364 }
365 assert_eq!(
366 Config::toml_example(),
367 r##"# Config.a should be a number
368a = 7
369
370# Config.b should be a string
371b = "seven"
372
373c = "default"
374
375e = ["default",]
376
377f = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
378
379g = ["super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
380"second", "third",]
381
382# Config.color should be a hex color code
383color = "#FAFAFA"
384
385"##
386 );
387 }
388
389 #[test]
390 fn no_nesting() {
391 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
393 #[allow(dead_code)]
394 struct Inner {
395 a: usize,
397 }
398 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
399 #[allow(dead_code)]
400 struct Outer {
401 inner: Inner,
403 }
404 assert_eq!(
405 Outer::toml_example(),
406 r#"# Outer.inner is a complex struct
407inner = ""
408
409"#
410 );
411 }
412
413 #[test]
414 fn nesting() {
415 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
417 #[allow(dead_code)]
418 struct Inner {
419 a: usize,
421 }
422 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
423 #[allow(dead_code)]
424 struct Outer {
425 #[toml_example(nesting)]
427 inner: Inner,
428 }
429 assert_eq!(
430 Outer::toml_example(),
431 r#"# Outer.inner is a complex struct
432# Inner is a config live in Outer
433[inner]
434# Inner.a should be a number
435a = 0
436
437"#
438 );
439 assert_eq!(
440 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
441 Outer::default()
442 );
443 }
444
445 #[test]
446 fn nesting_by_section() {
447 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
449 #[allow(dead_code)]
450 struct Inner {
451 a: usize,
453 }
454 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
455 #[allow(dead_code)]
456 struct Outer {
457 #[toml_example(nesting = section)]
459 inner: Inner,
460 }
461 assert_eq!(
462 Outer::toml_example(),
463 r#"# Outer.inner is a complex struct
464# Inner is a config live in Outer
465[inner]
466# Inner.a should be a number
467a = 0
468
469"#
470 );
471 assert_eq!(
472 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
473 Outer::default()
474 );
475 }
476
477 #[test]
478 fn nesting_by_prefix() {
479 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
481 #[allow(dead_code)]
482 struct Inner {
483 a: usize,
485 }
486 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
487 #[allow(dead_code)]
488 struct Outer {
489 #[toml_example(nesting = prefix)]
491 inner: Inner,
492 }
493 assert_eq!(
494 Outer::toml_example(),
495 r#"# Outer.inner is a complex struct
496# Inner is a config live in Outer
497# Inner.a should be a number
498inner.a = 0
499
500"#
501 );
502 assert_eq!(
503 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
504 Outer::default()
505 );
506 }
507
508 #[test]
509 fn nesting_vector() {
510 #[derive(TomlExample, Deserialize)]
512 #[allow(dead_code)]
513 struct Service {
514 port: usize,
516 }
517 #[derive(TomlExample, Deserialize)]
518 #[allow(dead_code)]
519 struct Node {
520 #[toml_example(nesting)]
522 services: Vec<Service>,
523 }
524 assert_eq!(
525 Node::toml_example(),
526 r#"# Services are running in the node
527# Service with specific port
528[[services]]
529# port should be a number
530port = 0
531
532"#
533 );
534 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
535 }
536
537 #[test]
538 fn nesting_hashmap() {
539 #[derive(TomlExample, Deserialize)]
541 #[allow(dead_code)]
542 struct Service {
543 port: usize,
545 }
546 #[derive(TomlExample, Deserialize)]
547 #[allow(dead_code)]
548 struct Node {
549 #[toml_example(nesting)]
551 services: HashMap<String, Service>,
552 }
553 assert_eq!(
554 Node::toml_example(),
555 r#"# Services are running in the node
556# Service with specific port
557[services.example]
558# port should be a number
559port = 0
560
561"#
562 );
563 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
564 }
565
566 #[test]
567 fn optional_nesting() {
568 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
570 #[allow(dead_code)]
571 struct Inner {
572 a: usize,
574 }
575 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
576 #[allow(dead_code)]
577 struct Outer {
578 #[toml_example(nesting)]
580 inner: Option<Inner>,
581 }
582 assert_eq!(
583 Outer::toml_example(),
584 r#"# Outer.inner is a complex struct
585# Inner is a config live in Outer
586# [inner]
587# Inner.a should be a number
588# a = 0
589
590"#
591 );
592 assert_eq!(
593 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
594 Outer::default()
595 );
596 }
597
598 #[test]
599 fn optional_nesting_by_section() {
600 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
602 #[allow(dead_code)]
603 struct Inner {
604 a: usize,
606 }
607 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
608 #[allow(dead_code)]
609 struct Outer {
610 #[toml_example(nesting = section)]
612 inner: Option<Inner>,
613 }
614 assert_eq!(
615 Outer::toml_example(),
616 r#"# Outer.inner is a complex struct
617# Inner is a config live in Outer
618# [inner]
619# Inner.a should be a number
620# a = 0
621
622"#
623 );
624 assert_eq!(
625 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
626 Outer::default()
627 );
628 }
629
630 #[test]
631 fn optional_nesting_by_prefix() {
632 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
634 #[allow(dead_code)]
635 struct Inner {
636 a: usize,
638 }
639 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
640 #[allow(dead_code)]
641 struct Outer {
642 #[toml_example(nesting = prefix)]
644 inner: Option<Inner>,
645 }
646 assert_eq!(
647 Outer::toml_example(),
648 r#"# Outer.inner is a complex struct
649# Inner is a config live in Outer
650# Inner.a should be a number
651# inner.a = 0
652
653"#
654 );
655 assert_eq!(
656 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
657 Outer::default()
658 );
659 }
660
661 #[test]
662 fn optional_nesting_vector() {
663 #[derive(TomlExample, Deserialize)]
665 #[allow(dead_code)]
666 struct Service {
667 port: usize,
669 }
670 #[derive(TomlExample, Deserialize)]
671 #[allow(dead_code)]
672 struct Node {
673 #[toml_example(nesting)]
675 services: Option<Vec<Service>>,
676 }
677 assert_eq!(
678 Node::toml_example(),
679 r#"# Services are running in the node
680# Service with specific port
681# [[services]]
682# port should be a number
683# port = 0
684
685"#
686 );
687 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
688 }
689
690 #[test]
691 fn optional_nesting_hashmap() {
692 #[derive(TomlExample, Deserialize)]
694 #[allow(dead_code)]
695 struct Service {
696 port: usize,
698 }
699 #[derive(TomlExample, Deserialize)]
700 #[allow(dead_code)]
701 struct Node {
702 #[toml_example(nesting)]
704 services: Option<HashMap<String, Service>>,
705 }
706 assert_eq!(
707 Node::toml_example(),
708 r#"# Services are running in the node
709# Service with specific port
710# [services.example]
711# port should be a number
712# port = 0
713
714"#
715 );
716 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
717 }
718
719 #[test]
720 fn nesting_hashmap_with_default_name() {
721 #[derive(TomlExample, Deserialize)]
723 #[allow(dead_code)]
724 struct Service {
725 #[toml_example(default = 80)]
727 port: usize,
728 }
729 #[derive(TomlExample, Deserialize)]
730 #[allow(dead_code)]
731 struct Node {
732 #[toml_example(nesting)]
734 #[toml_example(default = http)]
735 services: HashMap<String, Service>,
736 }
737 assert_eq!(
738 Node::toml_example(),
739 r#"# Services are running in the node
740# Service with specific port
741[services.http]
742# port should be a number
743port = 80
744
745"#
746 );
747 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
748 }
749
750 #[test]
751 fn nesting_hashmap_with_dash_name() {
752 #[derive(TomlExample, Deserialize)]
754 #[allow(dead_code)]
755 struct Service {
756 #[toml_example(default = 80)]
758 port: usize,
759 }
760 #[derive(TomlExample, Deserialize)]
761 #[allow(dead_code)]
762 struct Node {
763 #[toml_example(nesting)]
765 #[toml_example(default = http.01)]
766 services: HashMap<String, Service>,
767 }
768 assert_eq!(
769 Node::toml_example(),
770 r#"# Services are running in the node
771# Service with specific port
772[services.http-01]
773# port should be a number
774port = 80
775
776"#
777 );
778 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
779 }
780
781 #[test]
782 fn require() {
783 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
784 #[allow(dead_code)]
785 struct Config {
786 #[toml_example(require)]
788 a: Option<usize>,
789 #[toml_example(require)]
791 b: Option<String>,
792 #[toml_example(require)]
793 #[toml_example(default = "third")]
794 c: Option<String>,
795 }
796 assert_eq!(
797 Config::toml_example(),
798 r#"# Config.a is an optional number
799a = 0
800
801# Config.b is an optional string
802b = ""
803
804c = "third"
805
806"#
807 );
808 }
809
810 #[test]
811 fn skip() {
812 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
813 #[allow(dead_code)]
814 struct Config {
815 a: usize,
817 #[toml_example(skip)]
818 b: usize,
819 #[serde(skip)]
820 c: usize,
821 #[serde(skip_deserializing)]
822 d: usize,
823 }
824 assert_eq!(
825 Config::toml_example(),
826 r#"# Config.a is a number
827a = 0
828
829"#
830 );
831 }
832
833 #[test]
834 fn is_enum() {
835 fn b() -> AB {
836 AB::B
837 }
838 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
839 #[allow(dead_code)]
840 struct Config {
841 #[toml_example(enum)]
843 #[toml_example(default)]
844 ab: AB,
845 #[toml_example(is_enum)]
847 #[serde(default)]
848 ab2: AB,
849 #[toml_example(is_enum)]
851 #[serde(default = "b")]
852 ab3: AB,
853 }
854 #[derive(Debug, Default, Deserialize, PartialEq)]
855 enum AB {
856 #[default]
857 A,
858 B,
859 }
860 assert_eq!(
861 Config::toml_example(),
862 r#"# Config.ab is an enum
863ab = "A"
864
865# Config.ab2 is an enum too
866ab2 = "A"
867
868# Config.ab3 is an enum as well
869ab3 = "B"
870
871"#
872 );
873 }
874
875 #[test]
876 fn r_sharp_field() {
877 #[derive(TomlExample)]
878 #[allow(dead_code)]
879 struct Config {
880 r#type: usize,
882 }
883 assert_eq!(
884 Config::toml_example(),
885 r#"# Config.type is a number
886type = 0
887
888"#
889 );
890 }
891
892 #[test]
893 fn non_nesting_field_should_be_first() {
894 #[derive(TomlExample)]
895 #[allow(dead_code)]
896 struct Foo {
897 a: String,
898 }
899
900 #[derive(TomlExample)]
901 #[allow(dead_code)]
902 struct Bar {
903 #[toml_example(nesting)]
904 foo: Foo,
905 b: String,
906 }
907
908 assert_eq!(
909 Bar::toml_example(),
910 r#"b = ""
911
912[foo]
913a = ""
914
915"#
916 );
917 }
918
919 #[test]
920 fn rename() {
921 use serde::Serialize;
922
923 #[derive(Deserialize, Serialize, TomlExample)]
924 struct Config {
925 #[serde(rename = "bb")]
926 b: usize,
927 }
928 assert_eq!(
929 Config::toml_example(),
930 r#"bb = 0
931
932"#
933 );
934 }
935
936 #[test]
937 fn rename_all() {
938 use serde::Serialize;
939
940 #[derive(Deserialize, Serialize, TomlExample)]
941 #[serde(rename_all = "kebab-case")]
942 struct Config {
943 a_a: usize,
944 }
945 assert_eq!(
946 Config::toml_example(),
947 r#"a-a = 0
948
949"#
950 );
951 }
952
953 #[test]
954 fn hashset_and_struct() {
955 use std::collections::HashMap;
956
957 #[derive(TomlExample)]
958 #[allow(dead_code)]
959 struct Foo {
960 a: String,
961 }
962
963 #[derive(TomlExample)]
964 #[allow(dead_code)]
965 struct Bar {
966 #[toml_example(nesting)]
968 default: Foo,
969
970 #[toml_example(nesting)]
972 instance: HashMap<String, Foo>,
973 }
974
975 assert_eq!(
976 Bar::toml_example(),
977 r#"# Default instances doc
978[default]
979a = ""
980
981# Instances doc
982[instance.example]
983a = ""
984
985"#
986 );
987 }
988}