cuprated/config/
macros.rs

1use toml_edit::TableLike;
2
3/// A macro for config structs defined in `cuprated`. This macro generates a function that
4/// can insert toml comments created from doc comments on fields.
5///
6/// # Attributes
7/// - `#[flatten = true]`: lets the writer know that the field is flattened into the parent struct.
8/// - `#[child = true]`: writes the doc comments for all fields in the child struct.
9/// - `#[inline = true]`: inlines the struct into `{}` instead of having a separate `[]` header.
10/// - `#[comment_out = true]`: comments out the field.
11///
12/// # Documentation
13/// Consider using the following style when adding documentation:
14///
15/// ```rust
16/// struct Config {
17///     /// BRIEF DESCRIPTION.
18///     ///
19///     /// (optional) LONGER DESCRIPTION.
20//      ///
21///     /// Type         | (optional) FIELD TYPE
22///     /// Valid values | EXPRESSION REPRESENTING VALID VALUES
23///     /// Examples     | (optional) A FEW EXAMPLE VALUES
24///     field: (),
25/// }
26/// ```
27///
28/// For example:
29/// ```rust
30/// struct Config {
31///     /// Enable/disable fast sync.
32///     ///
33///     /// Fast sync skips verification of old blocks by
34///     /// comparing block hashes to a built-in hash file,
35///     /// disabling this will significantly increase sync time.
36///     /// New blocks are still fully validated.
37///     ///
38///     /// Type         | boolean
39///     /// Valid values | true, false
40///     fast_sync: bool,
41/// }
42/// ```
43///
44/// Language for types:
45///
46/// | Rust type    | Wording used in user-book |
47/// |--------------|---------------------------|
48/// | bool         | boolean
49/// | u{8-64}      | Number
50/// | i{8-64}      | Signed number
51/// | f{32,64}     | Floating point number
52/// | str, String  | String
53/// | enum, struct | `DataStructureName` (e.g. `Duration`) or $DESCRIPTION (e.g. `IP address`)
54///
55/// If some fields are redundant or unnecessary, do not add them.
56///
57/// # Field documentation length
58/// In order to prevent wrapping/scrollbars in the user book and in editors,
59/// add newlines when a documentation line crosses ~70 characters, around this long:
60///
61/// `----------------------------------------------------------------------`
62macro_rules! config_struct {
63    (
64        $(#[$meta:meta])*
65        pub struct $name:ident {
66            $(
67                $(#[flatten = $flat:literal])?
68                $(#[child = $child:literal])?
69                $(#[inline = $inline:literal])?
70                $(#[comment_out = $comment_out:literal])?
71                $(#[doc = $doc:expr])*
72                $(##[$field_meta:meta])*
73                pub $field:ident: $field_ty:ty,
74            )*
75        }
76    ) => {
77        $(#[$meta])*
78        pub struct $name {
79            $(
80                $(#[doc = $doc])*
81                $(#[$field_meta])*
82                pub $field: $field_ty,
83            )*
84        }
85
86        impl $name {
87            #[allow(unused_labels, clippy::allow_attributes)]
88            pub fn write_docs(doc: &mut dyn ::toml_edit::TableLike) {
89                $(
90
91                    'write_field: {
92                        let key_str = &stringify!($field);
93
94                        let mut field_prefix = [ $(
95                          format!("##{}\n", $doc),
96                        )*].concat();
97
98                        $(
99                        if $comment_out {
100                            field_prefix.push('#');
101                        }
102                        )?
103
104                        $(
105                        if $flat {
106                            <$field_ty>::write_docs(doc);
107                            break 'write_field;
108                        }
109                        )?
110
111                        $(
112                        if $child {
113                            <$field_ty>::write_docs(doc.get_key_value_mut(&key_str).unwrap().1.as_table_like_mut().unwrap());
114                        }
115                        )?
116
117                        if let Some(table) = doc.entry(&key_str).or_insert_with(|| panic!()).as_table_mut() {
118                            $(
119                                if $inline {
120                                    let mut table = table.clone().into_inline_table();
121                                    doc.insert(&key_str, ::toml_edit::Item::Value(::toml_edit::Value::InlineTable(table)));
122                                    doc.key_mut(&key_str).unwrap().leaf_decor_mut().set_prefix(field_prefix);
123                                    break 'write_field;
124                                }
125                            )?
126                            table.decor_mut().set_prefix(format!("\n{}", field_prefix));
127                        }else {
128                            doc.key_mut(&key_str).unwrap().leaf_decor_mut().set_prefix(field_prefix);
129                        }
130                    }
131                )*
132            }
133        }
134    };
135}
136
137pub(crate) use config_struct;