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 nestring 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(nesting)]
72//!     #[toml_example(default = http)]
73//!     services: HashMap<String, Service>,
74//! }
75//!
76//! assert_eq!(Node::toml_example(),
77//! r#"# Services are running in the node
78//! ## Service with specific port
79//! [services.http]
80//! ## port should be a number
81//! port = 80
82//!
83//! "#);
84//! ```
85//!
86//! The fields of a struct can inherit their defaults from the parent struct when the
87//! `#[toml_example(default)]`, `#[serde(default)]` or `#[serde(default = "default_fn")]`
88//! attribute is set as an outer attribute of the parent struct:
89//!
90//! ```rust
91//! use serde::Serialize;
92//! use toml_example::TomlExample;
93//!
94//! #[derive(TomlExample, Serialize)]
95//! #[serde(default)]
96//! struct Config {
97//!     /// Name of the theme to use
98//!     theme: String,
99//! }
100//!
101//! impl Default for Config {
102//!     fn default() -> Self {
103//!         Self {
104//!             theme: String::from("Dark"),
105//!         }
106//!     }
107//! }
108//!
109//! assert_eq!(Config::toml_example(),
110//! r#"# Name of the theme to use
111//! theme = "Dark"
112//!
113//! "#);
114//! ```
115//!
116//! If you want an optional field become a required field in example,
117//! place the `#[toml_example(require)]` on the field.
118//! If you want to skip some field you can use `#[toml_example(skip)]`,
119//! the `#[serde(skip)]`, `#[serde(skip_deserializing)]` also works.
120//! ```rust
121//! use toml_example::TomlExample;
122//! #[derive(TomlExample)]
123//! struct Config {
124//!     /// Config.a is an optional number
125//!     #[toml_example(require)]
126//!     a: Option<usize>,
127//!     /// Config.b is an optional string
128//!     #[toml_example(require)]
129//!     b: Option<String>,
130//!     #[toml_example(require)]
131//!     #[toml_example(default = "third")]
132//!     c: Option<String>,
133//!     #[toml_example(skip)]
134//!     d: usize,
135//! }
136//! assert_eq!(Config::toml_example(),
137//! r#"# Config.a is an optional number
138//! a = 0
139//!
140//! ## Config.b is an optional string
141//! b = ""
142//!
143//! c = "third"
144//!
145//! "#)
146//! ```
147//!
148//! You can also use fieldless enums, but you have to annotate them with `#[toml_example(enum)]` or
149//! `#[toml_example(is_enum)]` if you mind the keyword highlight you likely get when writing
150//! "enum".<br>
151//! When annotating a field with `#[toml_example(default)]` it will use the
152//! [Debug](core::fmt::Debug) implementation.
153//! However for non-TOML datatypes like enums, this does not work as the value needs to be treated
154//! as a string in TOML. The `#[toml_example(enum)]` attribute just adds the needed quotes around
155//! the [Debug](core::fmt::Debug) implementation and can be omitted if a custom
156//! [Debug](core::fmt::Debug) already includes those.
157//! ```rust
158//! use toml_example::TomlExample;
159//! #[derive(TomlExample)]
160//! struct Config {
161//!     /// Config.priority is an enum
162//!     #[toml_example(default)]
163//!     #[toml_example(enum)]
164//!     priority: Priority,
165//! }
166//! #[derive(Debug, Default)]
167//! enum Priority {
168//!     #[default]
169//!     Important,
170//!     Trivial,
171//! }
172//! assert_eq!(Config::toml_example(),
173//! r#"# Config.priority is an enum
174//! priority = "Important"
175//!
176//! "#)
177//! ```
178
179#[doc(hidden)]
180pub use toml_example_derive::TomlExample;
181pub mod traits;
182pub use traits::*;
183
184#[cfg(test)]
185mod tests {
186    use crate as toml_example;
187    use serde_derive::Deserialize;
188    use std::collections::HashMap;
189    use toml_example::TomlExample;
190
191    #[test]
192    fn basic() {
193        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
194        #[allow(dead_code)]
195        struct Config {
196            /// Config.a should be a number
197            a: usize,
198            /// Config.b should be a string
199            b: String,
200        }
201        assert_eq!(
202            Config::toml_example(),
203            r#"# Config.a should be a number
204a = 0
205
206# Config.b should be a string
207b = ""
208
209"#
210        );
211        assert_eq!(
212            toml::from_str::<Config>(&Config::toml_example()).unwrap(),
213            Config::default()
214        );
215        let mut tmp_file = std::env::temp_dir();
216        tmp_file.push("config.toml");
217        Config::to_toml_example(&tmp_file.as_path().to_str().unwrap()).unwrap();
218        assert_eq!(
219            std::fs::read_to_string(tmp_file).unwrap(),
220            r#"# Config.a should be a number
221a = 0
222
223# Config.b should be a string
224b = ""
225
226"#
227        );
228    }
229
230    #[test]
231    fn option() {
232        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
233        #[allow(dead_code)]
234        struct Config {
235            /// Config.a is an optional number
236            a: Option<usize>,
237            /// Config.b is an optional string
238            b: Option<String>,
239        }
240        assert_eq!(
241            Config::toml_example(),
242            r#"# Config.a is an optional number
243# a = 0
244
245# Config.b is an optional string
246# b = ""
247
248"#
249        );
250        assert_eq!(
251            toml::from_str::<Config>(&Config::toml_example()).unwrap(),
252            Config::default()
253        )
254    }
255
256    #[test]
257    fn vec() {
258        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
259        #[allow(dead_code)]
260        struct Config {
261            /// Config.a is a list of number
262            a: Vec<usize>,
263            /// Config.b is a list of string
264            b: Vec<String>,
265            /// Config.c
266            c: Vec<Option<usize>>,
267            /// Config.d
268            d: Option<Vec<usize>>,
269        }
270        assert_eq!(
271            Config::toml_example(),
272            r#"# Config.a is a list of number
273a = [ 0, ]
274
275# Config.b is a list of string
276b = [ "", ]
277
278# Config.c
279c = [ 0, ]
280
281# Config.d
282# d = [ 0, ]
283
284"#
285        );
286        assert!(toml::from_str::<Config>(&Config::toml_example()).is_ok())
287    }
288
289    #[test]
290    fn struct_doc() {
291        /// Config is to arrange something or change the controls on a computer or other device
292        /// so that it can be used in a particular way
293        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
294        #[allow(dead_code)]
295        struct Config {
296            /// Config.a should be a number
297            /// the number should be greater or equal zero
298            a: usize,
299        }
300        assert_eq!(
301            Config::toml_example(),
302            r#"# Config is to arrange something or change the controls on a computer or other device
303# so that it can be used in a particular way
304# Config.a should be a number
305# the number should be greater or equal zero
306a = 0
307
308"#
309        );
310        assert_eq!(
311            toml::from_str::<Config>(&Config::toml_example()).unwrap(),
312            Config::default()
313        )
314    }
315
316    #[test]
317    fn serde_default() {
318        fn default_a() -> usize {
319            7
320        }
321        fn default_b() -> String {
322            "default".into()
323        }
324        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
325        #[allow(dead_code)]
326        struct Config {
327            /// Config.a should be a number
328            #[serde(default = "default_a")]
329            a: usize,
330            /// Config.b should be a string
331            #[serde(default = "default_b")]
332            b: String,
333            /// Config.c should be a number
334            #[serde(default)]
335            c: usize,
336            /// Config.d should be a string
337            #[serde(default)]
338            d: String,
339            #[serde(default)]
340            e: Option<usize>,
341        }
342        assert_eq!(
343            Config::toml_example(),
344            r#"# Config.a should be a number
345a = 7
346
347# Config.b should be a string
348b = "default"
349
350# Config.c should be a number
351c = 0
352
353# Config.d should be a string
354d = ""
355
356# e = 0
357
358"#
359        );
360    }
361
362    #[test]
363    fn toml_example_default() {
364        fn default_str() -> String {
365            "seven".into()
366        }
367        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
368        #[allow(dead_code)]
369        struct Config {
370            /// Config.a should be a number
371            #[toml_example(default = 7)]
372            a: usize,
373            /// Config.b should be a string
374            #[toml_example(default = "default")]
375            #[serde(default = "default_str")]
376            b: String,
377            #[serde(default = "default_str")]
378            #[toml_example(default = "default")]
379            c: String,
380            #[toml_example(default = [ "default", ])]
381            e: Vec<String>,
382            #[toml_example(
383                default = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
384            )]
385            f: String,
386            #[toml_example(default = [ "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
387               "second",
388               "third",
389            ])]
390            g: Vec<String>,
391            /// Config.color should be a hex color code
392            #[toml_example(default = "#FAFAFA")]
393            color: String,
394        }
395        assert_eq!(
396            Config::toml_example(),
397            r##"# Config.a should be a number
398a = 7
399
400# Config.b should be a string
401b = "seven"
402
403c = "default"
404
405e = ["default",]
406
407f = "super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
408
409g = ["super looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string",
410"second", "third",]
411
412# Config.color should be a hex color code
413color = "#FAFAFA"
414
415"##
416        );
417    }
418
419    #[test]
420    fn struct_serde_default() {
421        #[derive(TomlExample, Deserialize, PartialEq)]
422        #[serde(default)]
423        struct Foo {
424            bar: String,
425            #[serde(default)]
426            x: usize,
427        }
428        impl Default for Foo {
429            fn default() -> Self {
430                Foo {
431                    bar: String::from("hello world"),
432                    x: 12,
433                }
434            }
435        }
436        assert_eq!(
437            Foo::toml_example(),
438            r##"bar = "hello world"
439
440x = 0
441
442"##
443        );
444    }
445
446    #[test]
447    fn struct_serde_default_fn() {
448        #[derive(TomlExample, Deserialize, PartialEq)]
449        #[serde(default = "default")]
450        struct Foo {
451            bar: String,
452            #[toml_example(default = "field override")]
453            baz: String,
454        }
455        fn default() -> Foo {
456            Foo {
457                bar: String::from("hello world"),
458                baz: String::from("custom default"),
459            }
460        }
461        assert_eq!(
462            Foo::toml_example(),
463            r##"bar = "hello world"
464
465baz = "field override"
466
467"##
468        );
469    }
470
471    #[test]
472    fn struct_toml_example_default() {
473        #[derive(TomlExample, Deserialize, PartialEq)]
474        #[toml_example(default)]
475        struct Foo {
476            #[serde(default = "paru")]
477            yay: &'static str,
478            aur_is_useful: bool,
479        }
480        impl Default for Foo {
481            fn default() -> Self {
482                Foo {
483                    yay: "yay!",
484                    aur_is_useful: true,
485                }
486            }
487        }
488        fn paru() -> &'static str {
489            "no, paru!"
490        }
491        assert_eq!(
492            Foo::toml_example(),
493            r##"yay = "no, paru!"
494
495aur_is_useful = true
496
497"##
498        );
499    }
500
501    #[test]
502    fn no_nesting() {
503        /// Inner is a config live in Outer
504        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
505        #[allow(dead_code)]
506        struct Inner {
507            /// Inner.a should be a number
508            a: usize,
509        }
510        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
511        #[allow(dead_code)]
512        struct Outer {
513            /// Outer.inner is a complex struct
514            inner: Inner,
515        }
516        assert_eq!(
517            Outer::toml_example(),
518            r#"# Outer.inner is a complex struct
519inner = ""
520
521"#
522        );
523    }
524
525    #[test]
526    fn nesting() {
527        /// Inner is a config live in Outer
528        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
529        #[allow(dead_code)]
530        struct Inner {
531            /// Inner.a should be a number
532            a: usize,
533        }
534        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
535        #[allow(dead_code)]
536        struct Outer {
537            /// Outer.inner is a complex struct
538            #[toml_example(nesting)]
539            inner: Inner,
540        }
541        assert_eq!(
542            Outer::toml_example(),
543            r#"# Outer.inner is a complex struct
544# Inner is a config live in Outer
545[inner]
546# Inner.a should be a number
547a = 0
548
549"#
550        );
551        assert_eq!(
552            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
553            Outer::default()
554        );
555    }
556
557    #[test]
558    fn nesting_by_section() {
559        /// Inner is a config live in Outer
560        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
561        #[allow(dead_code)]
562        struct Inner {
563            /// Inner.a should be a number
564            a: usize,
565        }
566        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
567        #[allow(dead_code)]
568        struct Outer {
569            /// Outer.inner is a complex struct
570            #[toml_example(nesting = section)]
571            inner: Inner,
572        }
573        assert_eq!(
574            Outer::toml_example(),
575            r#"# Outer.inner is a complex struct
576# Inner is a config live in Outer
577[inner]
578# Inner.a should be a number
579a = 0
580
581"#
582        );
583        assert_eq!(
584            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
585            Outer::default()
586        );
587    }
588
589    #[test]
590    fn nesting_by_prefix() {
591        /// Inner is a config live in Outer
592        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
593        #[allow(dead_code)]
594        struct Inner {
595            /// Inner.a should be a number
596            a: usize,
597        }
598        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
599        #[allow(dead_code)]
600        struct Outer {
601            /// Outer.inner is a complex struct
602            #[toml_example(nesting = prefix)]
603            inner: Inner,
604        }
605        assert_eq!(
606            Outer::toml_example(),
607            r#"# Outer.inner is a complex struct
608# Inner is a config live in Outer
609# Inner.a should be a number
610inner.a = 0
611
612"#
613        );
614        assert_eq!(
615            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
616            Outer::default()
617        );
618    }
619
620    #[test]
621    fn nesting_vector() {
622        /// Service with specific port
623        #[derive(TomlExample, Deserialize)]
624        #[allow(dead_code)]
625        struct Service {
626            /// port should be a number
627            port: usize,
628        }
629        #[derive(TomlExample, Deserialize)]
630        #[allow(dead_code)]
631        struct Node {
632            /// Services are running in the node
633            #[toml_example(nesting)]
634            services: Vec<Service>,
635        }
636        assert_eq!(
637            Node::toml_example(),
638            r#"# Services are running in the node
639# Service with specific port
640[[services]]
641# port should be a number
642port = 0
643
644"#
645        );
646        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
647    }
648
649    #[test]
650    fn nesting_hashmap() {
651        /// Service with specific port
652        #[derive(TomlExample, Deserialize)]
653        #[allow(dead_code)]
654        struct Service {
655            /// port should be a number
656            port: usize,
657        }
658        #[derive(TomlExample, Deserialize)]
659        #[allow(dead_code)]
660        struct Node {
661            /// Services are running in the node
662            #[toml_example(nesting)]
663            services: HashMap<String, Service>,
664        }
665        assert_eq!(
666            Node::toml_example(),
667            r#"# Services are running in the node
668# Service with specific port
669[services.example]
670# port should be a number
671port = 0
672
673"#
674        );
675        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
676    }
677
678    #[test]
679    fn optional_nesting() {
680        /// Inner is a config live in Outer
681        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
682        #[allow(dead_code)]
683        struct Inner {
684            /// Inner.a should be a number
685            a: usize,
686        }
687        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
688        #[allow(dead_code)]
689        struct Outer {
690            /// Outer.inner is a complex struct
691            #[toml_example(nesting)]
692            inner: Option<Inner>,
693        }
694        assert_eq!(
695            Outer::toml_example(),
696            r#"# Outer.inner is a complex struct
697# Inner is a config live in Outer
698# [inner]
699# Inner.a should be a number
700# a = 0
701
702"#
703        );
704        assert_eq!(
705            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
706            Outer::default()
707        );
708    }
709
710    #[test]
711    fn optional_nesting_by_section() {
712        /// Inner is a config live in Outer
713        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
714        #[allow(dead_code)]
715        struct Inner {
716            /// Inner.a should be a number
717            a: usize,
718        }
719        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
720        #[allow(dead_code)]
721        struct Outer {
722            /// Outer.inner is a complex struct
723            #[toml_example(nesting = section)]
724            inner: Option<Inner>,
725        }
726        assert_eq!(
727            Outer::toml_example(),
728            r#"# Outer.inner is a complex struct
729# Inner is a config live in Outer
730# [inner]
731# Inner.a should be a number
732# a = 0
733
734"#
735        );
736        assert_eq!(
737            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
738            Outer::default()
739        );
740    }
741
742    #[test]
743    fn optional_nesting_by_prefix() {
744        /// Inner is a config live in Outer
745        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
746        #[allow(dead_code)]
747        struct Inner {
748            /// Inner.a should be a number
749            a: usize,
750        }
751        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
752        #[allow(dead_code)]
753        struct Outer {
754            /// Outer.inner is a complex struct
755            #[toml_example(nesting = prefix)]
756            inner: Option<Inner>,
757        }
758        assert_eq!(
759            Outer::toml_example(),
760            r#"# Outer.inner is a complex struct
761# Inner is a config live in Outer
762# Inner.a should be a number
763# inner.a = 0
764
765"#
766        );
767        assert_eq!(
768            toml::from_str::<Outer>(&Outer::toml_example()).unwrap(),
769            Outer::default()
770        );
771    }
772
773    #[test]
774    fn optional_nesting_vector() {
775        /// Service with specific port
776        #[derive(TomlExample, Deserialize)]
777        #[allow(dead_code)]
778        struct Service {
779            /// port should be a number
780            port: usize,
781        }
782        #[derive(TomlExample, Deserialize)]
783        #[allow(dead_code)]
784        struct Node {
785            /// Services are running in the node
786            #[toml_example(nesting)]
787            services: Option<Vec<Service>>,
788        }
789        assert_eq!(
790            Node::toml_example(),
791            r#"# Services are running in the node
792# Service with specific port
793# [[services]]
794# port should be a number
795# port = 0
796
797"#
798        );
799        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
800    }
801
802    #[test]
803    fn optional_nesting_hashmap() {
804        /// Service with specific port
805        #[derive(TomlExample, Deserialize)]
806        #[allow(dead_code)]
807        struct Service {
808            /// port should be a number
809            port: usize,
810        }
811        #[derive(TomlExample, Deserialize)]
812        #[allow(dead_code)]
813        struct Node {
814            /// Services are running in the node
815            #[toml_example(nesting)]
816            services: Option<HashMap<String, Service>>,
817        }
818        assert_eq!(
819            Node::toml_example(),
820            r#"# Services are running in the node
821# Service with specific port
822# [services.example]
823# port should be a number
824# port = 0
825
826"#
827        );
828        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
829    }
830
831    #[test]
832    fn nesting_hashmap_with_default_name() {
833        /// Service with specific port
834        #[derive(TomlExample, Deserialize)]
835        #[allow(dead_code)]
836        struct Service {
837            /// port should be a number
838            #[toml_example(default = 80)]
839            port: usize,
840        }
841        #[derive(TomlExample, Deserialize)]
842        #[allow(dead_code)]
843        struct Node {
844            /// Services are running in the node
845            #[toml_example(nesting)]
846            #[toml_example(default = http)]
847            services: HashMap<String, Service>,
848        }
849        assert_eq!(
850            Node::toml_example(),
851            r#"# Services are running in the node
852# Service with specific port
853[services.http]
854# port should be a number
855port = 80
856
857"#
858        );
859        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
860    }
861
862    #[test]
863    fn nesting_hashmap_with_dash_name() {
864        /// Service with specific port
865        #[derive(TomlExample, Deserialize)]
866        #[allow(dead_code)]
867        struct Service {
868            /// port should be a number
869            #[toml_example(default = 80)]
870            port: usize,
871        }
872        #[derive(TomlExample, Deserialize)]
873        #[allow(dead_code)]
874        struct Node {
875            /// Services are running in the node
876            #[toml_example(nesting)]
877            #[toml_example(default = http.01)]
878            services: HashMap<String, Service>,
879        }
880        assert_eq!(
881            Node::toml_example(),
882            r#"# Services are running in the node
883# Service with specific port
884[services.http-01]
885# port should be a number
886port = 80
887
888"#
889        );
890        assert!(toml::from_str::<Node>(&Node::toml_example()).is_ok());
891    }
892
893    #[test]
894    fn require() {
895        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
896        #[allow(dead_code)]
897        struct Config {
898            /// Config.a is an optional number
899            #[toml_example(require)]
900            a: Option<usize>,
901            /// Config.b is an optional string
902            #[toml_example(require)]
903            b: Option<String>,
904            #[toml_example(require)]
905            #[toml_example(default = "third")]
906            c: Option<String>,
907        }
908        assert_eq!(
909            Config::toml_example(),
910            r#"# Config.a is an optional number
911a = 0
912
913# Config.b is an optional string
914b = ""
915
916c = "third"
917
918"#
919        );
920    }
921
922    #[test]
923    fn skip() {
924        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
925        #[allow(dead_code)]
926        struct Config {
927            /// Config.a is a number
928            a: usize,
929            #[toml_example(skip)]
930            b: usize,
931            #[serde(skip)]
932            c: usize,
933            #[serde(skip_deserializing)]
934            d: usize,
935        }
936        assert_eq!(
937            Config::toml_example(),
938            r#"# Config.a is a number
939a = 0
940
941"#
942        );
943    }
944
945    #[test]
946    fn is_enum() {
947        fn b() -> AB {
948            AB::B
949        }
950        #[derive(TomlExample, Deserialize, Default, PartialEq, Debug)]
951        #[allow(dead_code)]
952        struct Config {
953            /// Config.ab is an enum
954            #[toml_example(enum)]
955            #[toml_example(default)]
956            ab: AB,
957            /// Config.ab2 is an enum too
958            #[toml_example(is_enum)]
959            #[serde(default)]
960            ab2: AB,
961            /// Config.ab3 is an enum as well
962            #[toml_example(is_enum)]
963            #[serde(default = "b")]
964            ab3: AB,
965        }
966        #[derive(Debug, Default, Deserialize, PartialEq)]
967        enum AB {
968            #[default]
969            A,
970            B,
971        }
972        assert_eq!(
973            Config::toml_example(),
974            r#"# Config.ab is an enum
975ab = "A"
976
977# Config.ab2 is an enum too
978ab2 = "A"
979
980# Config.ab3 is an enum as well
981ab3 = "B"
982
983"#
984        );
985    }
986
987    #[test]
988    fn r_sharp_field() {
989        #[derive(TomlExample)]
990        #[allow(dead_code)]
991        struct Config {
992            /// Config.type is a number
993            r#type: usize,
994        }
995        assert_eq!(
996            Config::toml_example(),
997            r#"# Config.type is a number
998type = 0
999
1000"#
1001        );
1002    }
1003
1004    #[test]
1005    fn non_nesting_field_should_be_first() {
1006        #[derive(TomlExample)]
1007        #[allow(dead_code)]
1008        struct Foo {
1009            a: String,
1010        }
1011
1012        #[derive(TomlExample)]
1013        #[allow(dead_code)]
1014        struct Bar {
1015            #[toml_example(nesting)]
1016            foo: Foo,
1017            b: String,
1018        }
1019
1020        assert_eq!(
1021            Bar::toml_example(),
1022            r#"b = ""
1023
1024[foo]
1025a = ""
1026
1027"#
1028        );
1029    }
1030
1031    #[test]
1032    fn rename() {
1033        use serde::Serialize;
1034
1035        #[derive(Deserialize, Serialize, TomlExample)]
1036        struct Config {
1037            #[serde(rename = "bb")]
1038            b: usize,
1039        }
1040        assert_eq!(
1041            Config::toml_example(),
1042            r#"bb = 0
1043
1044"#
1045        );
1046    }
1047
1048    #[test]
1049    fn rename_all() {
1050        use serde::Serialize;
1051
1052        #[derive(Deserialize, Serialize, TomlExample)]
1053        #[serde(rename_all = "kebab-case")]
1054        struct Config {
1055            a_a: usize,
1056        }
1057        assert_eq!(
1058            Config::toml_example(),
1059            r#"a-a = 0
1060
1061"#
1062        );
1063    }
1064
1065    #[test]
1066    fn hashset_and_struct() {
1067        use std::collections::HashMap;
1068
1069        #[derive(TomlExample)]
1070        #[allow(dead_code)]
1071        struct Foo {
1072            a: String,
1073        }
1074
1075        #[derive(TomlExample)]
1076        #[allow(dead_code)]
1077        struct Bar {
1078            /// Default instances doc
1079            #[toml_example(nesting)]
1080            default: Foo,
1081
1082            /// Instances doc
1083            #[toml_example(nesting)]
1084            instance: HashMap<String, Foo>,
1085        }
1086
1087        assert_eq!(
1088            Bar::toml_example(),
1089            r#"# Default instances doc
1090[default]
1091a = ""
1092
1093# Instances doc
1094[instance.example]
1095a = ""
1096
1097"#
1098        );
1099    }
1100}