1#[doc(hidden)]
119pub use toml_example_derive::TomlExample;
120pub mod traits;
121pub use traits::*;
122
123#[cfg(test)]
124mod tests {
125 use crate as toml_example;
126 use serde_derive::Deserialize;
127 use std::collections::HashMap;
128 use toml_example::TomlExample;
129
130 #[test]
131 fn basic() {
132 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
133 #[allow(dead_code)]
134 struct Config {
135 a: usize,
137 b: String,
139 }
140 assert_eq!(
141 Config::toml_example(),
142 r#"# Config.a should be a number
143a = 0
144
145# Config.b should be a string
146b = ""
147
148"#
149 );
150 assert_eq!(
151 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
152 Config::default()
153 );
154 let mut tmp_file = std::env::temp_dir();
155 tmp_file.push("config.toml");
156 Config::to_toml_example(&tmp_file.as_path().to_str().unwrap()).unwrap();
157 assert_eq!(
158 std::fs::read_to_string(tmp_file).unwrap(),
159 r#"# Config.a should be a number
160a = 0
161
162# Config.b should be a string
163b = ""
164
165"#
166 );
167 }
168
169 #[test]
170 fn option() {
171 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
172 #[allow(dead_code)]
173 struct Config {
174 a: Option<usize>,
176 b: Option<String>,
178 }
179 assert_eq!(
180 Config::toml_example(),
181 r#"# Config.a is an optional number
182# a = 0
183
184# Config.b is an optional string
185# b = ""
186
187"#
188 );
189 assert_eq!(
190 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
191 Config::default()
192 )
193 }
194
195 #[test]
196 fn vec() {
197 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
198 #[allow(dead_code)]
199 struct Config {
200 a: Vec<usize>,
202 b: Vec<String>,
204 c: Vec<Option<usize>>,
206 d: Option<Vec<usize>>,
208 }
209 assert_eq!(
210 Config::toml_example(),
211 r#"# Config.a is a list of number
212a = [ 0, ]
213
214# Config.b is a list of string
215b = [ "", ]
216
217# Config.c
218c = [ 0, ]
219
220# Config.d
221# d = [ 0, ]
222
223"#
224 );
225 assert!(toml::from_str::<Config>(&Config::toml_example()).is_ok())
226 }
227
228 #[test]
229 fn struct_doc() {
230 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
233 #[allow(dead_code)]
234 struct Config {
235 a: usize,
238 }
239 assert_eq!(
240 Config::toml_example(),
241 r#"# Config is to arrange something or change the controls on a computer or other device
242# so that it can be used in a particular way
243# Config.a should be a number
244# the number should be greater or equal zero
245a = 0
246
247"#
248 );
249 assert_eq!(
250 toml::from_str::<Config>(&Config::toml_example()).unwrap(),
251 Config::default()
252 )
253 }
254
255 #[test]
256 fn serde_default() {
257 fn default_a() -> usize {
258 7
259 }
260 fn default_b() -> String {
261 "default".into()
262 }
263 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
264 #[allow(dead_code)]
265 struct Config {
266 #[serde(default = "default_a")]
268 a: usize,
269 #[serde(default = "default_b")]
271 b: String,
272 #[serde(default)]
274 c: usize,
275 #[serde(default)]
277 d: String,
278 #[serde(default)]
279 e: Option<usize>,
280 }
281 assert_eq!(
282 Config::toml_example(),
283 r#"# Config.a should be a number
284a = 7
285
286# Config.b should be a string
287b = "default"
288
289# Config.c should be a number
290c = 0
291
292# Config.d should be a string
293d = ""
294
295# e = 0
296
297"#
298 );
299 }
300
301 #[test]
302 fn toml_example_default() {
303 fn default_str() -> String {
304 "seven".into()
305 }
306 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
307 #[allow(dead_code)]
308 struct Config {
309 #[toml_example(default = 7)]
311 a: usize,
312 #[toml_example(default = "default")]
314 #[serde(default = "default_str")]
315 b: String,
316 #[serde(default = "default_str")]
317 #[toml_example(default = "default")]
318 c: String,
319 #[toml_example(default = [ "default", ])]
320 e: Vec<String>,
321 #[toml_example(
322 default = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
323 )]
324 f: String,
325 #[toml_example(default = [ "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
326 "second",
327 "third",
328 ])]
329 g: Vec<String>,
330 #[toml_example(default = "#FAFAFA")]
332 color: String,
333 }
334 assert_eq!(
335 Config::toml_example(),
336 r##"# Config.a should be a number
337a = 7
338
339# Config.b should be a string
340b = "seven"
341
342c = "default"
343
344e = ["default",]
345
346f = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
347
348g = ["super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
349"second", "third",]
350
351# Config.color should be a hex color code
352color = "#FAFAFA"
353
354"##
355 );
356 }
357
358 #[test]
359 fn no_nesting() {
360 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
362 #[allow(dead_code)]
363 struct Inner {
364 a: usize,
366 }
367 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
368 #[allow(dead_code)]
369 struct Outer {
370 inner: Inner,
372 }
373 assert_eq!(
374 Outer::toml_example(),
375 r#"# Outer.inner is a complex struct
376inner = ""
377
378"#
379 );
380 }
381
382 #[test]
383 fn nesting() {
384 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
386 #[allow(dead_code)]
387 struct Inner {
388 a: usize,
390 }
391 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
392 #[allow(dead_code)]
393 struct Outer {
394 #[toml_example(nesting)]
396 inner: Inner,
397 }
398 assert_eq!(
399 Outer::toml_example(),
400 r#"# Outer.inner is a complex struct
401# Inner is a config live in Outer
402[inner]
403# Inner.a should be a number
404a = 0
405
406"#
407 );
408 assert_eq!(
409 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
410 Outer::default()
411 );
412 }
413
414 #[test]
415 fn nesting_by_section() {
416 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
418 #[allow(dead_code)]
419 struct Inner {
420 a: usize,
422 }
423 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
424 #[allow(dead_code)]
425 struct Outer {
426 #[toml_example(nesting = section)]
428 inner: Inner,
429 }
430 assert_eq!(
431 Outer::toml_example(),
432 r#"# Outer.inner is a complex struct
433# Inner is a config live in Outer
434[inner]
435# Inner.a should be a number
436a = 0
437
438"#
439 );
440 assert_eq!(
441 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
442 Outer::default()
443 );
444 }
445
446 #[test]
447 fn nesting_by_prefix() {
448 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
450 #[allow(dead_code)]
451 struct Inner {
452 a: usize,
454 }
455 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
456 #[allow(dead_code)]
457 struct Outer {
458 #[toml_example(nesting = prefix)]
460 inner: Inner,
461 }
462 assert_eq!(
463 Outer::toml_example(),
464 r#"# Outer.inner is a complex struct
465# Inner is a config live in Outer
466# Inner.a should be a number
467inner.a = 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_vector() {
479 #[derive(TomlExample, Deserialize)]
481 #[allow(dead_code)]
482 struct Service {
483 port: usize,
485 }
486 #[derive(TomlExample, Deserialize)]
487 #[allow(dead_code)]
488 struct Node {
489 #[toml_example(nesting)]
491 services: Vec<Service>,
492 }
493 assert_eq!(
494 Node::toml_example(),
495 r#"# Services are running in the node
496# Service with specific port
497[[services]]
498# port should be a number
499port = 0
500
501"#
502 );
503 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
504 }
505
506 #[test]
507 fn nesting_hashmap() {
508 #[derive(TomlExample, Deserialize)]
510 #[allow(dead_code)]
511 struct Service {
512 port: usize,
514 }
515 #[derive(TomlExample, Deserialize)]
516 #[allow(dead_code)]
517 struct Node {
518 #[toml_example(nesting)]
520 services: HashMap<String, Service>,
521 }
522 assert_eq!(
523 Node::toml_example(),
524 r#"# Services are running in the node
525# Service with specific port
526[services.example]
527# port should be a number
528port = 0
529
530"#
531 );
532 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
533 }
534
535 #[test]
536 fn optional_nesting() {
537 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
539 #[allow(dead_code)]
540 struct Inner {
541 a: usize,
543 }
544 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
545 #[allow(dead_code)]
546 struct Outer {
547 #[toml_example(nesting)]
549 inner: Option<Inner>,
550 }
551 assert_eq!(
552 Outer::toml_example(),
553 r#"# Outer.inner is a complex struct
554# Inner is a config live in Outer
555# [inner]
556# Inner.a should be a number
557# a = 0
558
559"#
560 );
561 assert_eq!(
562 toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
563 Outer::default()
564 );
565 }
566
567 #[test]
568 fn optional_nesting_by_section() {
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 = section)]
581 inner: Option<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
589# a = 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 optional_nesting_by_prefix() {
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 = prefix)]
613 inner: Option<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.a should be a number
620# inner.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_vector() {
632 #[derive(TomlExample, Deserialize)]
634 #[allow(dead_code)]
635 struct Service {
636 port: usize,
638 }
639 #[derive(TomlExample, Deserialize)]
640 #[allow(dead_code)]
641 struct Node {
642 #[toml_example(nesting)]
644 services: Option<Vec<Service>>,
645 }
646 assert_eq!(
647 Node::toml_example(),
648 r#"# Services are running in the node
649# Service with specific port
650# [[services]]
651# port should be a number
652# port = 0
653
654"#
655 );
656 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
657 }
658
659 #[test]
660 fn optional_nesting_hashmap() {
661 #[derive(TomlExample, Deserialize)]
663 #[allow(dead_code)]
664 struct Service {
665 port: usize,
667 }
668 #[derive(TomlExample, Deserialize)]
669 #[allow(dead_code)]
670 struct Node {
671 #[toml_example(nesting)]
673 services: Option<HashMap<String, Service>>,
674 }
675 assert_eq!(
676 Node::toml_example(),
677 r#"# Services are running in the node
678# Service with specific port
679# [services.example]
680# port should be a number
681# port = 0
682
683"#
684 );
685 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
686 }
687
688 #[test]
689 fn nesting_hashmap_with_default_name() {
690 #[derive(TomlExample, Deserialize)]
692 #[allow(dead_code)]
693 struct Service {
694 #[toml_example(default = 80)]
696 port: usize,
697 }
698 #[derive(TomlExample, Deserialize)]
699 #[allow(dead_code)]
700 struct Node {
701 #[toml_example(nesting)]
703 #[toml_example(default = http)]
704 services: 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.http]
711# port should be a number
712port = 80
713
714"#
715 );
716 assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
717 }
718
719 #[test]
720 fn nesting_hashmap_with_dash_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.01)]
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-01]
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 require() {
752 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
753 #[allow(dead_code)]
754 struct Config {
755 #[toml_example(require)]
757 a: Option<usize>,
758 #[toml_example(require)]
760 b: Option<String>,
761 #[toml_example(require)]
762 #[toml_example(default = "third")]
763 c: Option<String>,
764 }
765 assert_eq!(
766 Config::toml_example(),
767 r#"# Config.a is an optional number
768a = 0
769
770# Config.b is an optional string
771b = ""
772
773c = "third"
774
775"#
776 );
777 }
778
779 #[test]
780 fn skip() {
781 #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
782 #[allow(dead_code)]
783 struct Config {
784 a: usize,
786 #[toml_example(skip)]
787 b: usize,
788 #[serde(skip)]
789 c: usize,
790 #[serde(skip_deserializing)]
791 d: usize,
792 }
793 assert_eq!(
794 Config::toml_example(),
795 r#"# Config.a is a number
796a = 0
797
798"#
799 );
800 }
801
802 #[test]
803 fn r_sharp_field() {
804 #[derive(TomlExample)]
805 #[allow(dead_code)]
806 struct Config {
807 r#type: usize,
809 }
810 assert_eq!(
811 Config::toml_example(),
812 r#"# Config.type is a number
813type = 0
814
815"#
816 );
817 }
818
819 #[test]
820 fn non_nesting_field_should_be_first() {
821 #[derive(TomlExample)]
822 #[allow(dead_code)]
823 struct Foo {
824 a: String,
825 }
826
827 #[derive(TomlExample)]
828 #[allow(dead_code)]
829 struct Bar {
830 #[toml_example(nesting)]
831 foo: Foo,
832 b: String,
833 }
834
835 assert_eq!(
836 Bar::toml_example(),
837 r#"b = ""
838
839[foo]
840a = ""
841
842"#
843 );
844 }
845
846 #[test]
847 fn rename() {
848 use serde::Serialize;
849
850 #[derive(Deserialize, Serialize, TomlExample)]
851 struct Config {
852 #[serde(rename = "bb")]
853 b: usize,
854 }
855 assert_eq!(
856 Config::toml_example(),
857 r#"bb = 0
858
859"#
860 );
861 }
862
863 #[test]
864 fn rename_all() {
865 use serde::Serialize;
866
867 #[derive(Deserialize, Serialize, TomlExample)]
868 #[serde(rename_all = "kebab-case")]
869 struct Config {
870 a_a: usize,
871 }
872 assert_eq!(
873 Config::toml_example(),
874 r#"a-a = 0
875
876"#
877 );
878 }
879
880 #[test]
881 fn hashset_and_struct() {
882 use std::collections::HashMap;
883
884 #[derive(TomlExample)]
885 #[allow(dead_code)]
886 struct Foo {
887 a: String,
888 }
889
890 #[derive(TomlExample)]
891 #[allow(dead_code)]
892 struct Bar {
893 #[toml_example(nesting)]
895 default: Foo,
896
897 #[toml_example(nesting)]
899 instance: HashMap<String, Foo>,
900 }
901
902 assert_eq!(
903 Bar::toml_example(),
904 r#"# Default instances doc
905[default]
906a = ""
907
908# Instances doc
909[instance.example]
910a = ""
911
912"#
913 );
914 }
915}