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 require() {
937        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
938        #[allow(dead_code)]
939        struct Config {
940            /// Config.a is an optional number
941            #[toml_example(require)]
942            a: Option<usize>,
943            /// Config.b is an optional string
944            #[toml_example(require)]
945            b: Option<String>,
946            #[toml_example(require)]
947            #[toml_example(default = "third")]
948            c: Option<String>,
949        }
950        assert_eq!(
951            Config::toml_example(),
952            r#"# Config.a is an optional number
953a = 0
954
955# Config.b is an optional string
956b = ""
957
958c = "third"
959
960"#
961        );
962    }
963
964    #[test]
965    fn skip() {
966        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
967        #[allow(dead_code)]
968        struct Config {
969            /// Config.a is a number
970            a: usize,
971            #[toml_example(skip)]
972            b: usize,
973            #[serde(skip)]
974            c: usize,
975            #[serde(skip_deserializing)]
976            d: usize,
977        }
978        assert_eq!(
979            Config::toml_example(),
980            r#"# Config.a is a number
981a = 0
982
983"#
984        );
985    }
986
987    #[test]
988    fn is_enum() {
989        fn b() -> AB {
990            AB::B
991        }
992        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
993        #[allow(dead_code)]
994        struct Config {
995            /// Config.ab is an enum
996            #[toml_example(enum, default)]
997            ab: AB,
998            /// Config.ab2 is an enum too
999            #[toml_example(is_enum)]
1000            #[serde(default)]
1001            ab2: AB,
1002            /// Config.ab3 is an enum as well
1003            #[toml_example(is_enum)]
1004            #[serde(default = "b")]
1005            ab3: AB,
1006        }
1007        #[derive(Debug, Default, Deserialize, PartialEq)]
1008        enum AB {
1009            #[default]
1010            A,
1011            B,
1012        }
1013        assert_eq!(
1014            Config::toml_example(),
1015            r#"# Config.ab is an enum
1016ab = "A"
1017
1018# Config.ab2 is an enum too
1019ab2 = "A"
1020
1021# Config.ab3 is an enum as well
1022ab3 = "B"
1023
1024"#
1025        );
1026    }
1027
1028    #[test]
1029    fn flatten() {
1030        #[derive(TomlExample)]
1031        struct ItemWrapper {
1032            #[toml_example(flatten, nesting)]
1033            _item: Item,
1034        }
1035        #[derive(TomlExample)]
1036        struct Item {
1037            _value: String,
1038        }
1039        assert_eq!(ItemWrapper::toml_example(), Item::toml_example());
1040    }
1041
1042    #[test]
1043    fn multi_attr_escaping() {
1044        #[derive(TomlExample, Deserialize, PartialEq)]
1045        struct ConfigWrapper {
1046            #[toml_example(default = ["hello", "{nice :)\""], require)]
1047            vec: Option<Vec<String>>,
1048
1049            #[toml_example(require, default = ["\"\\\n}])", "super (fancy\\! :-) )"])]
1050            list: Option<[String; 2]>,
1051        }
1052
1053        assert_eq!(
1054            ConfigWrapper::toml_example(),
1055            r#"vec = ["hello", "{nice :)\""]
1056
1057list = ["\"\\\n}])", "super (fancy\\! :-) )"]
1058
1059"#
1060        );
1061    }
1062
1063    #[test]
1064    fn r_sharp_field() {
1065        #[derive(TomlExample)]
1066        #[allow(dead_code)]
1067        struct Config {
1068            /// Config.type is a number
1069            r#type: usize,
1070        }
1071        assert_eq!(
1072            Config::toml_example(),
1073            r#"# Config.type is a number
1074type = 0
1075
1076"#
1077        );
1078    }
1079
1080    #[test]
1081    fn non_nesting_field_should_be_first() {
1082        #[derive(TomlExample)]
1083        #[allow(dead_code)]
1084        struct Foo {
1085            a: String,
1086        }
1087
1088        #[derive(TomlExample)]
1089        #[allow(dead_code)]
1090        struct Bar {
1091            #[toml_example(nesting)]
1092            foo: Foo,
1093            b: String,
1094        }
1095
1096        assert_eq!(
1097            Bar::toml_example(),
1098            r#"b = ""
1099
1100[foo]
1101a = ""
1102
1103"#
1104        );
1105    }
1106
1107    #[test]
1108    fn rename() {
1109        use serde::Serialize;
1110
1111        #[derive(Deserialize, Serialize, TomlExample)]
1112        struct Config {
1113            #[serde(rename = "bb")]
1114            b: usize,
1115        }
1116        assert_eq!(
1117            Config::toml_example(),
1118            r#"bb = 0
1119
1120"#
1121        );
1122    }
1123
1124    #[test]
1125    fn rename_all() {
1126        use serde::Serialize;
1127
1128        #[derive(Deserialize, Serialize, TomlExample)]
1129        #[serde(rename_all = "kebab-case")]
1130        struct Config {
1131            a_a: usize,
1132        }
1133        assert_eq!(
1134            Config::toml_example(),
1135            r#"a-a = 0
1136
1137"#
1138        );
1139    }
1140
1141    #[test]
1142    fn hashset_and_struct() {
1143        use std::collections::HashMap;
1144
1145        #[derive(TomlExample)]
1146        #[allow(dead_code)]
1147        struct Foo {
1148            a: String,
1149        }
1150
1151        #[derive(TomlExample)]
1152        #[allow(dead_code)]
1153        struct Bar {
1154            /// Default instances doc
1155            #[toml_example(nesting)]
1156            default: Foo,
1157
1158            /// Instances doc
1159            #[toml_example(nesting)]
1160            instance: HashMap<String, Foo>,
1161        }
1162
1163        assert_eq!(
1164            Bar::toml_example(),
1165            r#"# Default instances doc
1166[default]
1167a = ""
1168
1169# Instances doc
1170[instance.example]
1171a = ""
1172
1173"#
1174        );
1175    }
1176}