toml_example/
lib.rs

1//! This crate provides the [`TomlExample`] trait and an accompanying derive macro.
2//!
3//! Deriving [`TomlExample`] on a struct will generate functions `toml_example()`, `to_toml_example(file_name)` for generating toml example content.
4//!
5//! The following code shows how `toml-example` can be used.
6//! ```rust
7//! use toml_example::TomlExample;
8//!
9//! /// Config is to arrange something or change the controls on a computer or other device
10//! /// so that it can be used in a particular way
11//! #[derive(TomlExample)]
12//! struct Config {
13//!     /// Config.a should be a number
14//!     a: usize,
15//!     /// Config.b should be a string
16//!     b: String,
17//!     /// Optional Config.c is a number
18//!     c: Option<usize>,
19//!     /// Config.d is a list of number
20//!     d: Vec<usize>,
21//!     #[toml_example(default =7)]
22//!     e: usize,
23//!     /// Config.f should be a string
24//!     #[toml_example(default = "seven")]
25//!     f: String,
26//! }
27//! assert_eq!( Config::toml_example(),
28//! r#"# Config is to arrange something or change the controls on a computer or other device
29//! ## so that it can be used in a particular way
30//! ## Config.a should be a number
31//! a = 0
32//!
33//! ## Config.b should be a string
34//! b = ""
35//!
36//! ## Optional Config.c is a number
37//! ## c = 0
38//!
39//! ## Config.d is a list of number
40//! d = [ 0, ]
41//!
42//! e = 7
43//!
44//! ## Config.f should be a string
45//! f = "seven"
46//!
47//! "#);
48//! ```
49//!
50//! Also, toml-example will use `#[serde(default)]`, `#[serde(default = "default_fn")]` for the
51//! example value.
52//!
53//! With nesting structure, `#[toml_example(nesting)]` should set on the field as following
54//! example.
55//!
56//! ```rust
57//! use std::collections::HashMap;
58//! use toml_example::TomlExample;
59//!
60//! /// Service with specific port
61//! #[derive(TomlExample)]
62//! struct Service {
63//!     /// port should be a number
64//!     #[toml_example(default = 80)]
65//!     port: usize,
66//! }
67//! #[derive(TomlExample)]
68//! #[allow(dead_code)]
69//! struct Node {
70//!     /// Services are running in the node
71//!     #[toml_example(default = http, nesting)]
72//!     services: HashMap<String, Service>,
73//! }
74//!
75//! assert_eq!(Node::toml_example(),
76//! r#"# Services are running in the node
77//! ## Service with specific port
78//! [services.http]
79//! ## port should be a number
80//! port = 80
81//!
82//! "#);
83//! ```
84//!
85//! Flattened items are supported as well.
86//!
87//! ```rust
88//! use toml_example::TomlExample;
89//!
90//! #[derive(TomlExample)]
91//! struct ItemWrapper {
92//!     #[toml_example(flatten, nesting)]
93//!     item: Item,
94//! }
95//! #[derive(TomlExample)]
96//! struct Item {
97//!     value: String,
98//! }
99//!
100//! assert_eq!(ItemWrapper::toml_example(), Item::toml_example());
101//! ```
102//!
103//! Flattening works with maps too!
104//!
105//! ```rust
106//! use serde::Deserialize;
107//! use toml_example::TomlExample;
108//! # use std::collections::HashMap;
109//!
110//! #[derive(TomlExample, Deserialize)]
111//! struct MainConfig {
112//!     #[serde(flatten)]
113//!     #[toml_example(nesting)]
114//!     nested: HashMap<String, ConfigItem>,
115//! }
116//! #[derive(TomlExample, Deserialize)]
117//! struct ConfigItem {
118//!     #[toml_example(default = false)]
119//!     enabled: bool,
120//! }
121//!
122//! let example = MainConfig::toml_example();
123//! assert!(toml::from_str::<MainConfig>(&example).is_ok());
124//! assert_eq!(example, r#"[example]
125//! enabled = false
126//!
127//! "#);
128//! ```
129//!
130//! The fields of a struct can inherit their defaults from the parent struct when the
131//! `#[toml_example(default)]`, `#[serde(default)]` or `#[serde(default = "default_fn")]`
132//! attribute is set as an outer attribute of the parent struct:
133//!
134//! ```rust
135//! use serde::Serialize;
136//! use toml_example::TomlExample;
137//!
138//! #[derive(TomlExample, Serialize)]
139//! #[serde(default)]
140//! struct Config {
141//!     /// Name of the theme to use
142//!     theme: String,
143//! }
144//!
145//! impl Default for Config {
146//!     fn default() -> Self {
147//!         Self {
148//!             theme: String::from("Dark"),
149//!         }
150//!     }
151//! }
152//!
153//! assert_eq!(Config::toml_example(),
154//! r#"# Name of the theme to use
155//! theme = "Dark"
156//!
157//! "#);
158//! ```
159//!
160//! If you want an optional field become a required field in example,
161//! place the `#[toml_example(require)]` on the field.
162//! If you want to skip some field you can use `#[toml_example(skip)]`,
163//! the `#[serde(skip)]`, `#[serde(skip_deserializing)]` also works.
164//! ```rust
165//! use toml_example::TomlExample;
166//! #[derive(TomlExample)]
167//! struct Config {
168//!     /// Config.a is an optional number
169//!     #[toml_example(require)]
170//!     a: Option<usize>,
171//!     /// Config.b is an optional string
172//!     #[toml_example(require)]
173//!     b: Option<String>,
174//!     #[toml_example(require, default = "third")]
175//!     c: Option<String>,
176//!     #[toml_example(skip)]
177//!     d: usize,
178//! }
179//! assert_eq!(Config::toml_example(),
180//! r#"# Config.a is an optional number
181//! a = 0
182//!
183//! ## Config.b is an optional string
184//! b = ""
185//!
186//! c = "third"
187//!
188//! "#)
189//! ```
190//!
191//! You can also use fieldless enums, but you have to annotate them with `#[toml_example(enum)]` or
192//! `#[toml_example(is_enum)]` if you mind the keyword highlight you likely get when writing
193//! "enum".<br>
194//! When annotating a field with `#[toml_example(default)]` it will use the
195//! [Debug](core::fmt::Debug) implementation.
196//! However for non-TOML data types like enums, this does not work as the value needs to be treated
197//! as a string in TOML. The `#[toml_example(enum)]` attribute just adds the needed quotes around
198//! the [Debug](core::fmt::Debug) implementation and can be omitted if a custom
199//! [Debug](core::fmt::Debug) already includes those.
200//! ```rust
201//! use toml_example::TomlExample;
202//! #[derive(TomlExample)]
203//! struct Config {
204//!     /// Config.priority is an enum
205//!     #[toml_example(default, enum)]
206//!     priority: Priority,
207//! }
208//! #[derive(Debug, Default)]
209//! enum Priority {
210//!     #[default]
211//!     Important,
212//!     Trivial,
213//! }
214//! assert_eq!(Config::toml_example(),
215//! r#"# Config.priority is an enum
216//! priority = "Important"
217//!
218//! "#)
219//! ```
220
221#[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            /// Config.a should be a number
239            a: usize,
240            /// Config.b should be a string
241            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            /// Config.a is an optional number
278            a: Option<usize>,
279            /// Config.b is an optional string
280            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            /// Config.a is a list of number
304            a: Vec<usize>,
305            /// Config.b is a list of string
306            b: Vec<String>,
307            /// Config.c
308            c: Vec<Option<usize>>,
309            /// Config.d
310            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        /// Config is to arrange something or change the controls on a computer or other device
334        /// so that it can be used in a particular way
335        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
336        #[allow(dead_code)]
337        struct Config {
338            /// Config.a should be a number
339            /// the number should be greater or equal zero
340            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            /// Config.a should be a number
370            #[serde(default = "default_a")]
371            a: usize,
372            /// Config.b should be a string
373            #[serde(default = "default_b")]
374            b: String,
375            /// Config.c should be a number
376            #[serde(default)]
377            c: usize,
378            /// Config.d should be a string
379            #[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            /// Config.a should be a number
413            #[toml_example(default = 7)]
414            a: usize,
415            /// Config.b should be a string
416            #[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            /// Config.color should be a hex color code
434            #[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        /// Inner is a config live in Outer
546        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
547        #[allow(dead_code)]
548        struct Inner {
549            /// Inner.a should be a number
550            a: usize,
551        }
552        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
553        #[allow(dead_code)]
554        struct Outer {
555            /// Outer.inner is a complex struct
556            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        /// Inner is a config live in Outer
570        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
571        #[allow(dead_code)]
572        struct Inner {
573            /// Inner.a should be a number
574            a: usize,
575        }
576        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
577        #[allow(dead_code)]
578        struct Outer {
579            /// Outer.inner is a complex struct
580            #[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        /// Inner is a config live in Outer
602        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
603        #[allow(dead_code)]
604        struct Inner {
605            /// Inner.a should be a number
606            a: usize,
607        }
608        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
609        #[allow(dead_code)]
610        struct Outer {
611            /// Outer.inner is a complex struct
612            #[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        /// Inner is a config live in Outer
634        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
635        #[allow(dead_code)]
636        struct Inner {
637            /// Inner.a should be a number
638            a: usize,
639        }
640        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
641        #[allow(dead_code)]
642        struct Outer {
643            /// Outer.inner is a complex struct
644            #[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        /// Service with specific port
665        #[derive(TomlExample, Deserialize)]
666        #[allow(dead_code)]
667        struct Service {
668            /// port should be a number
669            port: usize,
670        }
671        #[derive(TomlExample, Deserialize)]
672        #[allow(dead_code)]
673        struct Node {
674            /// Services are running in the node
675            #[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        /// Service with specific port
694        #[derive(TomlExample, Deserialize)]
695        #[allow(dead_code)]
696        struct Service {
697            /// port should be a number
698            port: usize,
699        }
700        #[derive(TomlExample, Deserialize)]
701        #[allow(dead_code)]
702        struct Node {
703            /// Services are running in the node
704            #[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        /// Inner is a config live in Outer
723        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
724        #[allow(dead_code)]
725        struct Inner {
726            /// Inner.a should be a number
727            a: usize,
728        }
729        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
730        #[allow(dead_code)]
731        struct Outer {
732            /// Outer.inner is a complex struct
733            #[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        /// Inner is a config live in Outer
755        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
756        #[allow(dead_code)]
757        struct Inner {
758            /// Inner.a should be a number
759            a: usize,
760        }
761        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
762        #[allow(dead_code)]
763        struct Outer {
764            /// Outer.inner is a complex struct
765            #[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        /// Inner is a config live in Outer
787        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
788        #[allow(dead_code)]
789        struct Inner {
790            /// Inner.a should be a number
791            a: usize,
792        }
793        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
794        #[allow(dead_code)]
795        struct Outer {
796            /// Outer.inner is a complex struct
797            #[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        /// Service with specific port
818        #[derive(TomlExample, Deserialize)]
819        #[allow(dead_code)]
820        struct Service {
821            /// port should be a number
822            port: usize,
823        }
824        #[derive(TomlExample, Deserialize)]
825        #[allow(dead_code)]
826        struct Node {
827            /// Services are running in the node
828            #[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        /// Service with specific port
847        #[derive(TomlExample, Deserialize)]
848        #[allow(dead_code)]
849        struct Service {
850            /// port should be a number
851            port: usize,
852        }
853        #[derive(TomlExample, Deserialize)]
854        #[allow(dead_code)]
855        struct Node {
856            /// Services are running in the node
857            #[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        /// Service with specific port
876        #[derive(TomlExample, Deserialize)]
877        #[allow(dead_code)]
878        struct Service {
879            /// port should be a number
880            #[toml_example(default = 80)]
881            port: usize,
882        }
883        #[derive(TomlExample, Deserialize)]
884        #[allow(dead_code)]
885        struct Node {
886            /// Services are running in the node
887            #[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        /// Service with specific port
907        #[derive(TomlExample, Deserialize)]
908        #[allow(dead_code)]
909        struct Service {
910            /// port should be a number
911            #[toml_example(default = 80)]
912            port: usize,
913        }
914        #[derive(TomlExample, Deserialize)]
915        #[allow(dead_code)]
916        struct Node {
917            /// Services are running in the node
918            #[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 recursive_nesting() {
937        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
938        struct Outer {
939            #[toml_example(nesting)]
940            _middle: Middle,
941        }
942        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
943        struct Middle {
944            #[toml_example(nesting)]
945            _inner: Inner,
946        }
947        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
948        struct Inner {
949            _value: usize,
950        }
951        let example = Outer::toml_example();
952        assert_eq!(toml::from_str::<Outer>(&example).unwrap(), Outer::default());
953        assert_eq!(
954            example,
955            r#"[_middle]
956[_middle._inner]
957_value = 0
958
959"#
960        );
961    }
962
963    #[test]
964    fn recursive_nesting_and_flatten() {
965        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
966        struct Outer {
967            #[toml_example(nesting)]
968            middle: Middle,
969            #[toml_example(default = false)]
970            /// Some toggle
971            flag: bool,
972        }
973        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
974        struct Middle {
975            #[serde(flatten)]
976            #[toml_example(nesting)]
977            /// Values of [Inner] are flattened into [Middle]
978            inner: Inner,
979        }
980        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
981        struct Inner {
982            #[toml_example(nesting)]
983            /// [Extra] is flattened into [Middle]
984            extra: Extra,
985            /// `value` is defined below `extra`, but shown above
986            value: usize,
987        }
988        #[derive(TomlExample, Debug, Deserialize, PartialEq)]
989        #[toml_example(default)]
990        struct Extra {
991            name: String,
992        }
993        impl Default for Extra {
994            fn default() -> Self {
995                Self {
996                    name: String::from("ferris"),
997                }
998            }
999        }
1000        let example = Outer::toml_example();
1001        assert_eq!(toml::from_str::<Outer>(&example).unwrap(), Outer::default());
1002        assert_eq!(
1003            example,
1004            r#"# Some toggle
1005flag = false
1006
1007[middle]
1008# Values of [Inner] are flattened into [Middle]
1009# `value` is defined below `extra`, but shown above
1010value = 0
1011
1012# [Extra] is flattened into [Middle]
1013[middle.extra]
1014name = "ferris"
1015
1016"#
1017        );
1018    }
1019
1020    #[test]
1021    fn require() {
1022        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
1023        #[allow(dead_code)]
1024        struct Config {
1025            /// Config.a is an optional number
1026            #[toml_example(require)]
1027            a: Option<usize>,
1028            /// Config.b is an optional string
1029            #[toml_example(require)]
1030            b: Option<String>,
1031            #[toml_example(require)]
1032            #[toml_example(default = "third")]
1033            c: Option<String>,
1034        }
1035        assert_eq!(
1036            Config::toml_example(),
1037            r#"# Config.a is an optional number
1038a = 0
1039
1040# Config.b is an optional string
1041b = ""
1042
1043c = "third"
1044
1045"#
1046        );
1047    }
1048
1049    #[test]
1050    fn skip() {
1051        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
1052        #[allow(dead_code)]
1053        struct Config {
1054            /// Config.a is a number
1055            a: usize,
1056            #[toml_example(skip)]
1057            b: usize,
1058            #[serde(skip)]
1059            c: usize,
1060            #[serde(skip_deserializing)]
1061            d: usize,
1062        }
1063        assert_eq!(
1064            Config::toml_example(),
1065            r#"# Config.a is a number
1066a = 0
1067
1068"#
1069        );
1070    }
1071
1072    #[test]
1073    fn is_enum() {
1074        fn b() -> AB {
1075            AB::B
1076        }
1077        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
1078        #[allow(dead_code)]
1079        struct Config {
1080            /// Config.ab is an enum
1081            #[toml_example(enum, default)]
1082            ab: AB,
1083            /// Config.ab2 is an enum too
1084            #[toml_example(is_enum)]
1085            #[serde(default)]
1086            ab2: AB,
1087            /// Config.ab3 is an enum as well
1088            #[toml_example(is_enum)]
1089            #[serde(default = "b")]
1090            ab3: AB,
1091        }
1092        #[derive(Debug, Default, Deserialize, PartialEq)]
1093        enum AB {
1094            #[default]
1095            A,
1096            B,
1097        }
1098        assert_eq!(
1099            Config::toml_example(),
1100            r#"# Config.ab is an enum
1101ab = "A"
1102
1103# Config.ab2 is an enum too
1104ab2 = "A"
1105
1106# Config.ab3 is an enum as well
1107ab3 = "B"
1108
1109"#
1110        );
1111    }
1112
1113    #[test]
1114    fn flatten() {
1115        #[derive(TomlExample)]
1116        struct ItemWrapper {
1117            #[toml_example(flatten, nesting)]
1118            _item: Item,
1119        }
1120        #[derive(TomlExample)]
1121        struct Item {
1122            _value: String,
1123        }
1124        assert_eq!(ItemWrapper::toml_example(), Item::toml_example());
1125    }
1126
1127    #[test]
1128    fn flatten_order() {
1129        #[derive(TomlExample)]
1130        struct Outer {
1131            #[toml_example(nesting)]
1132            _nested: Item,
1133            #[toml_example(flatten, nesting)]
1134            _flattened: Item,
1135        }
1136        #[derive(TomlExample)]
1137        struct Item {
1138            _value: String,
1139        }
1140        assert_eq!(
1141            Outer::toml_example(),
1142            r#"_value = ""
1143
1144[_nested]
1145_value = ""
1146
1147"#
1148        );
1149    }
1150
1151    #[test]
1152    fn multi_attr_escaping() {
1153        #[derive(TomlExample, Deserialize, PartialEq)]
1154        struct ConfigWrapper {
1155            #[toml_example(default = ["hello", "{nice :)\""], require)]
1156            vec: Option<Vec<String>>,
1157
1158            #[toml_example(require, default = ["\"\\\n}])", "super (fancy\\! :-) )"])]
1159            list: Option<[String; 2]>,
1160        }
1161
1162        assert_eq!(
1163            ConfigWrapper::toml_example(),
1164            r#"vec = ["hello", "{nice :)\""]
1165
1166list = ["\"\\\n}])", "super (fancy\\! :-) )"]
1167
1168"#
1169        );
1170    }
1171
1172    #[test]
1173    fn r_sharp_field() {
1174        #[derive(TomlExample)]
1175        #[allow(dead_code)]
1176        struct Config {
1177            /// Config.type is a number
1178            r#type: usize,
1179        }
1180        assert_eq!(
1181            Config::toml_example(),
1182            r#"# Config.type is a number
1183type = 0
1184
1185"#
1186        );
1187    }
1188
1189    #[test]
1190    fn non_nesting_field_should_be_first() {
1191        #[derive(TomlExample)]
1192        #[allow(dead_code)]
1193        struct Foo {
1194            a: String,
1195        }
1196
1197        #[derive(TomlExample)]
1198        #[allow(dead_code)]
1199        struct Bar {
1200            #[toml_example(nesting)]
1201            foo: Foo,
1202            b: String,
1203        }
1204
1205        assert_eq!(
1206            Bar::toml_example(),
1207            r#"b = ""
1208
1209[foo]
1210a = ""
1211
1212"#
1213        );
1214    }
1215
1216    #[test]
1217    fn rename() {
1218        use serde::Serialize;
1219
1220        #[derive(Deserialize, Serialize, TomlExample)]
1221        struct Config {
1222            #[serde(rename = "bb")]
1223            b: usize,
1224        }
1225        assert_eq!(
1226            Config::toml_example(),
1227            r#"bb = 0
1228
1229"#
1230        );
1231    }
1232
1233    #[test]
1234    fn rename_all() {
1235        use serde::Serialize;
1236
1237        #[derive(Deserialize, Serialize, TomlExample)]
1238        #[serde(rename_all = "kebab-case")]
1239        struct Config {
1240            a_a: usize,
1241        }
1242        assert_eq!(
1243            Config::toml_example(),
1244            r#"a-a = 0
1245
1246"#
1247        );
1248    }
1249
1250    #[test]
1251    fn hashset_and_struct() {
1252        use std::collections::HashMap;
1253
1254        #[derive(TomlExample)]
1255        #[allow(dead_code)]
1256        struct Foo {
1257            a: String,
1258        }
1259
1260        #[derive(TomlExample)]
1261        #[allow(dead_code)]
1262        struct Bar {
1263            /// Default instances doc
1264            #[toml_example(nesting)]
1265            default: Foo,
1266
1267            /// Instances doc
1268            #[toml_example(nesting)]
1269            instance: HashMap<String, Foo>,
1270        }
1271
1272        assert_eq!(
1273            Bar::toml_example(),
1274            r#"# Default instances doc
1275[default]
1276a = ""
1277
1278# Instances doc
1279[instance.example]
1280a = ""
1281
1282"#
1283        );
1284    }
1285
1286    #[test]
1287    fn optional_long_vector_field() {
1288        #[derive(TomlExample)]
1289        #[allow(dead_code)]
1290        struct Foo {
1291            /// Option<Vec<String>>, with small default values
1292            #[toml_example(default = ["a", "b", "c"])]
1293            array_value_example: Option<Vec<String>>,
1294
1295            /// Option<Vec<String>>, with long default values
1296            #[toml_example(default = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1297                "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 
1298                "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", 
1299                "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
1300            ])]
1301            array_long_value_example: Option<Vec<String>>,
1302
1303            /// Option<Vec<String>>, with a long default value but no space after comma
1304            #[toml_example(default = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"])]
1305            array_long_value_no_space_example: Option<Vec<String>>,
1306        }
1307        assert_eq!(
1308            Foo::toml_example(),
1309            r#"# Option<Vec<String>>, with small default values
1310# array_value_example = ["a", "b", "c"]
1311
1312# Option<Vec<String>>, with long default values
1313# array_long_value_example = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1314# "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
1315# "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
1316# "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"]
1317
1318# Option<Vec<String>>, with a long default value but no space after comma
1319# array_long_value_no_space_example = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"]
1320
1321"#
1322        );
1323    }
1324}