Skip to main content

setty_derive/
lib.rs

1mod attrs;
2mod combine;
3mod config;
4mod default;
5mod derive;
6
7/////////////////////////////////////////////////////////////////////////////////////////
8
9/// Our main workhorse. Derives the attributes based on the set of enabled crate features.
10///
11/// ## Field Attributes
12/// These arguments can be specified in `#[config(...)]` field attribute:
13/// * `default` - Use `Default::default` value if field is not present
14/// * `default = $expr` - Specifies expression used to initialize the value when it's not present in config
15/// * `default_str = "$str"` - Shorthand for`default = "$str".parse().unwrap()`
16/// * `combine(keep | replace | merge)` - Allows overriding how values are combined across different config files
17///   * Possible values:
18///     * `keep` - keeps first seen value
19///     * `replace` - fully replaces with the new value
20///     * `merge` - merges object keys and concatenates arrays, merge is smart and will not merge values across different enums
21///   * Default behavior:
22///     * `replace` for all known value types
23///     * `merge` for unknown types
24///       * You will need to implement `setty::combine::Combine` for it to work for custom types
25///       * `Config` derive macro automatically implements it for you
26///       * If you don't want any merging - simply override to use `combine(replace)`
27///
28/// ## Interaction with other attributes
29/// * `#[serde(...)]` attribute will be propagated and can be used to override default behaviour (e.g. `#[serde(tag = "type")]`)
30/// * `#[schemars(...)]` attribute will be propagated
31///
32#[proc_macro_derive(Config, attributes(config, serde, schemars))]
33pub fn derive_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
34    let input = syn::parse_macro_input!(input as syn::DeriveInput);
35
36    let combine_output = match combine::combine_impl(&input) {
37        Ok(output) => output,
38        Err(err) => return err.to_compile_error().into(),
39    };
40
41    let config_output = match config::config_impl(input) {
42        Ok(output) => output,
43        Err(err) => return err.to_compile_error().into(),
44    };
45
46    proc_macro::TokenStream::from(quote::quote! {
47        #config_output
48        #combine_output
49    })
50}
51
52/////////////////////////////////////////////////////////////////////////////////////////
53
54/// Special version of built-in `#[derive(Default)]` that recognizes `#[config(default = $expr)]` attributes
55#[proc_macro_derive(Default, attributes(config, default))]
56pub fn derive_default(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
57    let input = syn::parse_macro_input!(input as syn::DeriveInput);
58
59    match default::default_impl(input) {
60        Ok(output) => proc_macro::TokenStream::from(output),
61        Err(err) => err.to_compile_error().into(),
62    }
63}
64
65/////////////////////////////////////////////////////////////////////////////////////////
66
67/// Special version of `#[derive(..)]` macro. Works just like the standard one, except it
68/// will de-duplicate the derives expanded from [`Config`] and explicit ones.
69///
70/// Thus declaration such as `#[setty::derive(Config, Clone, serde::Deserialize)]` will
71/// always derive `Clone` and `Deserialize` even if those are not configured via top-level features,
72/// and will not duplicate implementations if those features were enabled.
73#[proc_macro_attribute]
74pub fn derive(
75    attr: proc_macro::TokenStream,
76    item: proc_macro::TokenStream,
77) -> proc_macro::TokenStream {
78    match derive::derive_impl(attr.into(), item.into()) {
79        Ok(output) => proc_macro::TokenStream::from(output),
80        Err(err) => err.to_compile_error().into(),
81    }
82}
83
84/////////////////////////////////////////////////////////////////////////////////////////
85
86#[proc_macro_attribute]
87pub fn __erase(
88    _attr: proc_macro::TokenStream,
89    _item: proc_macro::TokenStream,
90) -> proc_macro::TokenStream {
91    proc_macro::TokenStream::new()
92}
93
94/////////////////////////////////////////////////////////////////////////////////////////