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