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/////////////////////////////////////////////////////////////////////////////////////////