Skip to main content

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 hinden_doc() {
274        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
275        #[allow(dead_code)]
276        struct Config {
277            /// Config.a should be a number
278            // This doc will be hiddend not in the example
279            a: usize,
280
281            // NOTE: This note is only shown in the code
282            /// Config.b should be a string
283            b: String,
284        }
285        assert_eq!(
286            Config::toml_example(),
287            r#"# Config.a should be a number
288a = 0
289
290# Config.b should be a string
291b = ""
292
293"#
294        );
295    }
296
297    #[test]
298    fn option() {
299        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
300        #[allow(dead_code)]
301        struct Config {
302            /// Config.a is an optional number
303            a: Option<usize>,
304            /// Config.b is an optional string
305            b: Option<String>,
306        }
307        assert_eq!(
308            Config::toml_example(),
309            r#"# Config.a is an optional number
310# a = 0
311
312# Config.b is an optional string
313# b = ""
314
315"#
316        );
317        assert_eq!(
318            toml::from_str::<Config>(&Config::toml_example()).unwrap(),
319            Config::default()
320        )
321    }
322
323    #[test]
324    fn vec() {
325        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
326        #[allow(dead_code)]
327        struct Config {
328            /// Config.a is a list of number
329            a: Vec<usize>,
330            /// Config.b is a list of string
331            b: Vec<String>,
332            /// Config.c
333            c: Vec<Option<usize>>,
334            /// Config.d
335            d: Option<Vec<usize>>,
336        }
337        assert_eq!(
338            Config::toml_example(),
339            r#"# Config.a is a list of number
340a = [ 0, ]
341
342# Config.b is a list of string
343b = [ "", ]
344
345# Config.c
346c = [ 0, ]
347
348# Config.d
349# d = [ 0, ]
350
351"#
352        );
353        assert!(toml::from_str::<Config>(&Config::toml_example()).is_ok())
354    }
355
356    #[test]
357    fn hashset_btreeset() {
358        use std::collections::{BTreeSet, HashSet};
359
360        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
361        #[allow(dead_code)]
362        struct Config {
363            /// Config.a is a set of string
364            a: HashSet<String>,
365            /// Config.b is a set of number
366            b: BTreeSet<usize>,
367            /// Config.c is optional
368            c: Option<HashSet<String>>,
369        }
370        assert_eq!(
371            Config::toml_example(),
372            r#"# Config.a is a set of string
373a = [ "", ]
374
375# Config.b is a set of number
376b = [ 0, ]
377
378# Config.c is optional
379# c = [ "", ]
380
381"#
382        );
383        assert!(toml::from_str::<Config>(&Config::toml_example()).is_ok())
384    }
385
386    #[test]
387    fn struct_doc() {
388        /// Config is to arrange something or change the controls on a computer or other device
389        /// so that it can be used in a particular way
390        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
391        #[allow(dead_code)]
392        struct Config {
393            /// Config.a should be a number
394            /// the number should be greater or equal zero
395            a: usize,
396        }
397        assert_eq!(
398            Config::toml_example(),
399            r#"# Config is to arrange something or change the controls on a computer or other device
400# so that it can be used in a particular way
401# Config.a should be a number
402# the number should be greater or equal zero
403a = 0
404
405"#
406        );
407        assert_eq!(
408            toml::from_str::<Config>(&Config::toml_example()).unwrap(),
409            Config::default()
410        )
411    }
412
413    #[test]
414    fn serde_default() {
415        fn default_a() -> usize {
416            7
417        }
418        fn default_b() -> String {
419            "default".into()
420        }
421        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
422        #[allow(dead_code)]
423        struct Config {
424            /// Config.a should be a number
425            #[serde(default = "default_a")]
426            a: usize,
427            /// Config.b should be a string
428            #[serde(default = "default_b")]
429            b: String,
430            /// Config.c should be a number
431            #[serde(default)]
432            c: usize,
433            /// Config.d should be a string
434            #[serde(default)]
435            d: String,
436            #[serde(default)]
437            e: Option<usize>,
438        }
439        assert_eq!(
440            Config::toml_example(),
441            r#"# Config.a should be a number
442a = 7
443
444# Config.b should be a string
445b = "default"
446
447# Config.c should be a number
448c = 0
449
450# Config.d should be a string
451d = ""
452
453# e = 0
454
455"#
456        );
457    }
458
459    #[test]
460    fn toml_example_default() {
461        fn default_str() -> String {
462            "seven".into()
463        }
464        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
465        #[allow(dead_code)]
466        struct Config {
467            /// Config.a should be a number
468            #[toml_example(default = 7)]
469            a: usize,
470            /// Config.b should be a string
471            #[toml_example(default = "default")]
472            #[serde(default = "default_str")]
473            b: String,
474            #[serde(default = "default_str")]
475            #[toml_example(default = "default")]
476            c: String,
477            #[toml_example(default = [ "default", ])]
478            e: Vec<String>,
479            #[toml_example(
480                default = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
481            )]
482            f: String,
483            #[toml_example(default = [ "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
484               "second",
485               "third",
486            ])]
487            g: Vec<String>,
488            /// Config.color should be a hex color code
489            #[toml_example(default = "#FAFAFA")]
490            color: String,
491        }
492        assert_eq!(
493            Config::toml_example(),
494            r##"# Config.a should be a number
495a = 7
496
497# Config.b should be a string
498b = "seven"
499
500c = "default"
501
502e = ["default",]
503
504f = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
505
506g = ["super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
507"second", "third",]
508
509# Config.color should be a hex color code
510color = "#FAFAFA"
511
512"##
513        );
514    }
515
516    #[test]
517    fn struct_serde_default() {
518        #[derive(TomlExample, Deserialize, PartialEq)]
519        #[serde(default)]
520        struct Foo {
521            bar: String,
522            #[serde(default)]
523            x: usize,
524        }
525        impl Default for Foo {
526            fn default() -> Self {
527                Foo {
528                    bar: String::from("hello world"),
529                    x: 12,
530                }
531            }
532        }
533        assert_eq!(
534            Foo::toml_example(),
535            r##"bar = "hello world"
536
537x = 0
538
539"##
540        );
541    }
542
543    #[test]
544    fn struct_serde_default_fn() {
545        #[derive(TomlExample, Deserialize, PartialEq)]
546        #[serde(default = "default")]
547        struct Foo {
548            bar: String,
549            #[toml_example(default = "field override")]
550            baz: String,
551        }
552        fn default() -> Foo {
553            Foo {
554                bar: String::from("hello world"),
555                baz: String::from("custom default"),
556            }
557        }
558        assert_eq!(
559            Foo::toml_example(),
560            r##"bar = "hello world"
561
562baz = "field override"
563
564"##
565        );
566    }
567
568    #[test]
569    fn struct_toml_example_default() {
570        #[derive(TomlExample, Deserialize, PartialEq)]
571        #[toml_example(default)]
572        struct Foo {
573            #[serde(default = "paru")]
574            yay: &'static str,
575            aur_is_useful: bool,
576        }
577        impl Default for Foo {
578            fn default() -> Self {
579                Foo {
580                    yay: "yay!",
581                    aur_is_useful: true,
582                }
583            }
584        }
585        fn paru() -> &'static str {
586            "no, paru!"
587        }
588        assert_eq!(
589            Foo::toml_example(),
590            r##"yay = "no, paru!"
591
592aur_is_useful = true
593
594"##
595        );
596    }
597
598    #[test]
599    fn no_nesting() {
600        /// Inner is a config live in Outer
601        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
602        #[allow(dead_code)]
603        struct Inner {
604            /// Inner.a should be a number
605            a: usize,
606        }
607        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
608        #[allow(dead_code)]
609        struct Outer {
610            /// Outer.inner is a complex struct
611            inner: Inner,
612        }
613        assert_eq!(
614            Outer::toml_example(),
615            r#"# Outer.inner is a complex struct
616inner = ""
617
618"#
619        );
620    }
621
622    #[test]
623    fn nesting() {
624        /// Inner is a config live in Outer
625        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
626        #[allow(dead_code)]
627        struct Inner {
628            /// Inner.a should be a number
629            a: usize,
630        }
631        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
632        #[allow(dead_code)]
633        struct Outer {
634            /// Outer.inner is a complex struct
635            #[toml_example(nesting)]
636            inner: Inner,
637        }
638        assert_eq!(
639            Outer::toml_example(),
640            r#"# Outer.inner is a complex struct
641# Inner is a config live in Outer
642[inner]
643# Inner.a should be a number
644a = 0
645
646"#
647        );
648        assert_eq!(
649            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
650            Outer::default()
651        );
652    }
653
654    #[test]
655    fn nesting_by_section() {
656        /// Inner is a config live in Outer
657        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
658        #[allow(dead_code)]
659        struct Inner {
660            /// Inner.a should be a number
661            a: usize,
662        }
663        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
664        #[allow(dead_code)]
665        struct Outer {
666            /// Outer.inner is a complex struct
667            #[toml_example(nesting = section)]
668            inner: Inner,
669        }
670        assert_eq!(
671            Outer::toml_example(),
672            r#"# Outer.inner is a complex struct
673# Inner is a config live in Outer
674[inner]
675# Inner.a should be a number
676a = 0
677
678"#
679        );
680        assert_eq!(
681            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
682            Outer::default()
683        );
684    }
685
686    #[test]
687    fn nesting_by_prefix() {
688        /// Inner is a config live in Outer
689        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
690        #[allow(dead_code)]
691        struct Inner {
692            /// Inner.a should be a number
693            a: usize,
694        }
695        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
696        #[allow(dead_code)]
697        struct Outer {
698            /// Outer.inner is a complex struct
699            #[toml_example(nesting = prefix)]
700            inner: Inner,
701        }
702        assert_eq!(
703            Outer::toml_example(),
704            r#"# Outer.inner is a complex struct
705# Inner is a config live in Outer
706# Inner.a should be a number
707inner.a = 0
708
709"#
710        );
711        assert_eq!(
712            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
713            Outer::default()
714        );
715    }
716
717    #[test]
718    fn nesting_vector() {
719        /// Service with specific port
720        #[derive(TomlExample, Deserialize)]
721        #[allow(dead_code)]
722        struct Service {
723            /// port should be a number
724            port: usize,
725        }
726        #[derive(TomlExample, Deserialize)]
727        #[allow(dead_code)]
728        struct Node {
729            /// Services are running in the node
730            #[toml_example(nesting)]
731            services: Vec<Service>,
732        }
733        assert_eq!(
734            Node::toml_example(),
735            r#"# Services are running in the node
736# Service with specific port
737[[services]]
738# port should be a number
739port = 0
740
741"#
742        );
743        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
744    }
745
746    #[test]
747    fn nesting_hashmap() {
748        /// Service with specific port
749        #[derive(TomlExample, Deserialize)]
750        #[allow(dead_code)]
751        struct Service {
752            /// port should be a number
753            port: usize,
754        }
755        #[derive(TomlExample, Deserialize)]
756        #[allow(dead_code)]
757        struct Node {
758            /// Services are running in the node
759            #[toml_example(nesting)]
760            services: HashMap<String, Service>,
761        }
762        assert_eq!(
763            Node::toml_example(),
764            r#"# Services are running in the node
765# Service with specific port
766[services.example]
767# port should be a number
768port = 0
769
770"#
771        );
772        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
773    }
774
775    #[test]
776    fn optional_nesting() {
777        /// Inner is a config live in Outer
778        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
779        #[allow(dead_code)]
780        struct Inner {
781            /// Inner.a should be a number
782            a: usize,
783        }
784        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
785        #[allow(dead_code)]
786        struct Outer {
787            /// Outer.inner is a complex struct
788            #[toml_example(nesting)]
789            inner: Option<Inner>,
790        }
791        assert_eq!(
792            Outer::toml_example(),
793            r#"# Outer.inner is a complex struct
794# Inner is a config live in Outer
795# [inner]
796# Inner.a should be a number
797# a = 0
798
799"#
800        );
801        assert_eq!(
802            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
803            Outer::default()
804        );
805    }
806
807    #[test]
808    fn optional_nesting_by_section() {
809        /// Inner is a config live in Outer
810        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
811        #[allow(dead_code)]
812        struct Inner {
813            /// Inner.a should be a number
814            a: usize,
815        }
816        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
817        #[allow(dead_code)]
818        struct Outer {
819            /// Outer.inner is a complex struct
820            #[toml_example(nesting = section)]
821            inner: Option<Inner>,
822        }
823        assert_eq!(
824            Outer::toml_example(),
825            r#"# Outer.inner is a complex struct
826# Inner is a config live in Outer
827# [inner]
828# Inner.a should be a number
829# a = 0
830
831"#
832        );
833        assert_eq!(
834            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
835            Outer::default()
836        );
837    }
838
839    #[test]
840    fn optional_nesting_by_prefix() {
841        /// Inner is a config live in Outer
842        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
843        #[allow(dead_code)]
844        struct Inner {
845            /// Inner.a should be a number
846            a: usize,
847        }
848        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
849        #[allow(dead_code)]
850        struct Outer {
851            /// Outer.inner is a complex struct
852            #[toml_example(nesting = prefix)]
853            inner: Option<Inner>,
854        }
855        assert_eq!(
856            Outer::toml_example(),
857            r#"# Outer.inner is a complex struct
858# Inner is a config live in Outer
859# Inner.a should be a number
860# inner.a = 0
861
862"#
863        );
864        assert_eq!(
865            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
866            Outer::default()
867        );
868    }
869
870    #[test]
871    fn optional_nesting_vector() {
872        /// Service with specific port
873        #[derive(TomlExample, Deserialize)]
874        #[allow(dead_code)]
875        struct Service {
876            /// port should be a number
877            port: usize,
878        }
879        #[derive(TomlExample, Deserialize)]
880        #[allow(dead_code)]
881        struct Node {
882            /// Services are running in the node
883            #[toml_example(nesting)]
884            services: Option<Vec<Service>>,
885        }
886        assert_eq!(
887            Node::toml_example(),
888            r#"# Services are running in the node
889# Service with specific port
890# [[services]]
891# port should be a number
892# port = 0
893
894"#
895        );
896        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
897    }
898
899    #[test]
900    fn optional_nesting_hashmap() {
901        /// Service with specific port
902        #[derive(TomlExample, Deserialize)]
903        #[allow(dead_code)]
904        struct Service {
905            /// port should be a number
906            port: usize,
907        }
908        #[derive(TomlExample, Deserialize)]
909        #[allow(dead_code)]
910        struct Node {
911            /// Services are running in the node
912            #[toml_example(nesting)]
913            services: Option<HashMap<String, Service>>,
914        }
915        assert_eq!(
916            Node::toml_example(),
917            r#"# Services are running in the node
918# Service with specific port
919# [services.example]
920# port should be a number
921# port = 0
922
923"#
924        );
925        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
926    }
927
928    #[test]
929    fn nesting_hashmap_with_default_name() {
930        /// Service with specific port
931        #[derive(TomlExample, Deserialize)]
932        #[allow(dead_code)]
933        struct Service {
934            /// port should be a number
935            #[toml_example(default = 80)]
936            port: usize,
937        }
938        #[derive(TomlExample, Deserialize)]
939        #[allow(dead_code)]
940        struct Node {
941            /// Services are running in the node
942            #[toml_example(nesting)]
943            #[toml_example(default = http)]
944            services: HashMap<String, Service>,
945        }
946        assert_eq!(
947            Node::toml_example(),
948            r#"# Services are running in the node
949# Service with specific port
950[services.http]
951# port should be a number
952port = 80
953
954"#
955        );
956        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
957    }
958
959    #[test]
960    fn nesting_hashmap_with_dash_name() {
961        /// Service with specific port
962        #[derive(TomlExample, Deserialize)]
963        #[allow(dead_code)]
964        struct Service {
965            /// port should be a number
966            #[toml_example(default = 80)]
967            port: usize,
968        }
969        #[derive(TomlExample, Deserialize)]
970        #[allow(dead_code)]
971        struct Node {
972            /// Services are running in the node
973            #[toml_example(nesting)]
974            #[toml_example(default = http.01)]
975            services: HashMap<String, Service>,
976        }
977        assert_eq!(
978            Node::toml_example(),
979            r#"# Services are running in the node
980# Service with specific port
981[services.http-01]
982# port should be a number
983port = 80
984
985"#
986        );
987        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
988    }
989
990    #[test]
991    fn recursive_nesting() {
992        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
993        struct Outer {
994            #[toml_example(nesting)]
995            _middle: Middle,
996        }
997        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
998        struct Middle {
999            #[toml_example(nesting)]
1000            _inner: Inner,
1001        }
1002        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
1003        struct Inner {
1004            _value: usize,
1005        }
1006        let example = Outer::toml_example();
1007        assert_eq!(toml::from_str::<Outer>(&example).unwrap(), Outer::default());
1008        assert_eq!(
1009            example,
1010            r#"[_middle]
1011[_middle._inner]
1012_value = 0
1013
1014"#
1015        );
1016    }
1017
1018    #[test]
1019    fn recursive_nesting_and_flatten() {
1020        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
1021        struct Outer {
1022            #[toml_example(nesting)]
1023            middle: Middle,
1024            #[toml_example(default = false)]
1025            /// Some toggle
1026            flag: bool,
1027        }
1028        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
1029        struct Middle {
1030            #[serde(flatten)]
1031            #[toml_example(nesting)]
1032            /// Values of [Inner] are flattened into [Middle]
1033            inner: Inner,
1034        }
1035        #[derive(TomlExample, Default, Debug, Deserialize, PartialEq)]
1036        struct Inner {
1037            #[toml_example(nesting)]
1038            /// [Extra] is flattened into [Middle]
1039            extra: Extra,
1040            /// `value` is defined below `extra`, but shown above
1041            value: usize,
1042        }
1043        #[derive(TomlExample, Debug, Deserialize, PartialEq)]
1044        #[toml_example(default)]
1045        struct Extra {
1046            name: String,
1047        }
1048        impl Default for Extra {
1049            fn default() -> Self {
1050                Self {
1051                    name: String::from("ferris"),
1052                }
1053            }
1054        }
1055        let example = Outer::toml_example();
1056        assert_eq!(toml::from_str::<Outer>(&example).unwrap(), Outer::default());
1057        assert_eq!(
1058            example,
1059            r#"# Some toggle
1060flag = false
1061
1062[middle]
1063# Values of [Inner] are flattened into [Middle]
1064# `value` is defined below `extra`, but shown above
1065value = 0
1066
1067# [Extra] is flattened into [Middle]
1068[middle.extra]
1069name = "ferris"
1070
1071"#
1072        );
1073    }
1074
1075    #[test]
1076    fn require() {
1077        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
1078        #[allow(dead_code)]
1079        struct Config {
1080            /// Config.a is an optional number
1081            #[toml_example(require)]
1082            a: Option<usize>,
1083            /// Config.b is an optional string
1084            #[toml_example(require)]
1085            b: Option<String>,
1086            #[toml_example(require)]
1087            #[toml_example(default = "third")]
1088            c: Option<String>,
1089        }
1090        assert_eq!(
1091            Config::toml_example(),
1092            r#"# Config.a is an optional number
1093a = 0
1094
1095# Config.b is an optional string
1096b = ""
1097
1098c = "third"
1099
1100"#
1101        );
1102    }
1103
1104    #[test]
1105    fn skip() {
1106        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
1107        #[allow(dead_code)]
1108        struct Config {
1109            /// Config.a is a number
1110            a: usize,
1111            #[toml_example(skip)]
1112            b: usize,
1113            #[serde(skip)]
1114            c: usize,
1115            #[serde(skip_deserializing)]
1116            d: usize,
1117        }
1118        assert_eq!(
1119            Config::toml_example(),
1120            r#"# Config.a is a number
1121a = 0
1122
1123"#
1124        );
1125    }
1126
1127    #[test]
1128    fn is_enum() {
1129        fn b() -> AB {
1130            AB::B
1131        }
1132        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
1133        #[allow(dead_code)]
1134        struct Config {
1135            /// Config.ab is an enum
1136            #[toml_example(enum, default)]
1137            ab: AB,
1138            /// Config.ab2 is an enum too
1139            #[toml_example(is_enum)]
1140            #[serde(default)]
1141            ab2: AB,
1142            /// Config.ab3 is an enum as well
1143            #[toml_example(is_enum)]
1144            #[serde(default = "b")]
1145            ab3: AB,
1146        }
1147        #[derive(Debug, Default, Deserialize, PartialEq)]
1148        enum AB {
1149            #[default]
1150            A,
1151            B,
1152        }
1153        assert_eq!(
1154            Config::toml_example(),
1155            r#"# Config.ab is an enum
1156ab = "A"
1157
1158# Config.ab2 is an enum too
1159ab2 = "A"
1160
1161# Config.ab3 is an enum as well
1162ab3 = "B"
1163
1164"#
1165        );
1166    }
1167
1168    #[test]
1169    fn flatten() {
1170        #[derive(TomlExample)]
1171        struct ItemWrapper {
1172            #[toml_example(flatten, nesting)]
1173            _item: Item,
1174        }
1175        #[derive(TomlExample)]
1176        struct Item {
1177            _value: String,
1178        }
1179        assert_eq!(ItemWrapper::toml_example(), Item::toml_example());
1180    }
1181
1182    #[test]
1183    fn flatten_order() {
1184        #[derive(TomlExample)]
1185        struct Outer {
1186            #[toml_example(nesting)]
1187            _nested: Item,
1188            #[toml_example(flatten, nesting)]
1189            _flattened: Item,
1190        }
1191        #[derive(TomlExample)]
1192        struct Item {
1193            _value: String,
1194        }
1195        assert_eq!(
1196            Outer::toml_example(),
1197            r#"_value = ""
1198
1199[_nested]
1200_value = ""
1201
1202"#
1203        );
1204    }
1205
1206    #[test]
1207    fn multi_attr_escaping() {
1208        #[derive(TomlExample, Deserialize, PartialEq)]
1209        struct ConfigWrapper {
1210            #[toml_example(default = ["hello", "{nice :)\""], require)]
1211            vec: Option<Vec<String>>,
1212
1213            #[toml_example(require, default = ["\"\\\n}])", "super (fancy\\! :-) )"])]
1214            list: Option<[String; 2]>,
1215        }
1216
1217        assert_eq!(
1218            ConfigWrapper::toml_example(),
1219            r#"vec = ["hello", "{nice :)\""]
1220
1221list = ["\"\\\n}])", "super (fancy\\! :-) )"]
1222
1223"#
1224        );
1225    }
1226
1227    #[test]
1228    fn r_sharp_field() {
1229        #[derive(TomlExample)]
1230        #[allow(dead_code)]
1231        struct Config {
1232            /// Config.type is a number
1233            r#type: usize,
1234        }
1235        assert_eq!(
1236            Config::toml_example(),
1237            r#"# Config.type is a number
1238type = 0
1239
1240"#
1241        );
1242    }
1243
1244    #[test]
1245    fn non_nesting_field_should_be_first() {
1246        #[derive(TomlExample)]
1247        #[allow(dead_code)]
1248        struct Foo {
1249            a: String,
1250        }
1251
1252        #[derive(TomlExample)]
1253        #[allow(dead_code)]
1254        struct Bar {
1255            #[toml_example(nesting)]
1256            foo: Foo,
1257            b: String,
1258        }
1259
1260        assert_eq!(
1261            Bar::toml_example(),
1262            r#"b = ""
1263
1264[foo]
1265a = ""
1266
1267"#
1268        );
1269    }
1270
1271    #[test]
1272    fn rename() {
1273        use serde::Serialize;
1274
1275        #[derive(Deserialize, Serialize, TomlExample)]
1276        struct Config {
1277            #[serde(rename = "bb")]
1278            b: usize,
1279        }
1280        assert_eq!(
1281            Config::toml_example(),
1282            r#"bb = 0
1283
1284"#
1285        );
1286    }
1287
1288    #[test]
1289    fn rename_all() {
1290        use serde::Serialize;
1291
1292        #[derive(Deserialize, Serialize, TomlExample)]
1293        #[serde(rename_all = "kebab-case")]
1294        struct Config {
1295            a_a: usize,
1296        }
1297        assert_eq!(
1298            Config::toml_example(),
1299            r#"a-a = 0
1300
1301"#
1302        );
1303    }
1304
1305    #[test]
1306    fn hashset_and_struct() {
1307        use std::collections::HashMap;
1308
1309        #[derive(TomlExample)]
1310        #[allow(dead_code)]
1311        struct Foo {
1312            a: String,
1313        }
1314
1315        #[derive(TomlExample)]
1316        #[allow(dead_code)]
1317        struct Bar {
1318            /// Default instances doc
1319            #[toml_example(nesting)]
1320            default: Foo,
1321
1322            /// Instances doc
1323            #[toml_example(nesting)]
1324            instance: HashMap<String, Foo>,
1325        }
1326
1327        assert_eq!(
1328            Bar::toml_example(),
1329            r#"# Default instances doc
1330[default]
1331a = ""
1332
1333# Instances doc
1334[instance.example]
1335a = ""
1336
1337"#
1338        );
1339    }
1340
1341    #[test]
1342    fn optional_long_vector_field() {
1343        #[derive(TomlExample)]
1344        #[allow(dead_code)]
1345        struct Foo {
1346            /// Option<Vec<String>>, with small default values
1347            #[toml_example(default = ["a", "b", "c"])]
1348            array_value_example: Option<Vec<String>>,
1349
1350            /// Option<Vec<String>>, with long default values
1351            #[toml_example(default = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1352                "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 
1353                "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", 
1354                "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
1355            ])]
1356            array_long_value_example: Option<Vec<String>>,
1357
1358            /// Option<Vec<String>>, with a long default value but no space after comma
1359            #[toml_example(default = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"])]
1360            array_long_value_no_space_example: Option<Vec<String>>,
1361        }
1362        assert_eq!(
1363            Foo::toml_example(),
1364            r#"# Option<Vec<String>>, with small default values
1365# array_value_example = ["a", "b", "c"]
1366
1367# Option<Vec<String>>, with long default values
1368# array_long_value_example = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1369# "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
1370# "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
1371# "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"]
1372
1373# Option<Vec<String>>, with a long default value but no space after comma
1374# array_long_value_no_space_example = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"]
1375
1376"#
1377        );
1378    }
1379}