inline_config_macros/lib.rs
1//! Proc macro implementations for [`inline_config`].
2//!
3//! [`inline_config`]: https://docs.rs/inline-config/latest/inline_config/
4
5mod config;
6mod config_data;
7mod format;
8mod path;
9mod value;
10
11fn emit_tokens_or_error<T: quote::ToTokens>(result: syn::Result<T>) -> proc_macro::TokenStream {
12 match result {
13 Ok(output) => output.into_token_stream(),
14 Err(e) => e.to_compile_error(),
15 }
16 .into()
17}
18
19/// Declares a config module.
20///
21/// ```ignore
22/// #[config]
23/// <VIS> mod <IDENT> {
24/// <FORMAT>!(<SRC>);
25/// <FORMAT>!(<SRC>);
26/// <FORMAT>!(<SRC>);
27/// }
28/// ```
29/// When there are multiple sources, they got merged recursively per field, with latter ones overwriting former ones.
30/// All null values need to be overwritten eventually.
31///
32/// Every `<SRC>` shall be a literal string, or a macro invocation expanding into a literal string.
33/// The full support of eager expansion is impossible without nightly-only feature [`proc_macro_expand`].
34/// A subset of eager expansion for built-in macros is handled by [`macro_string`] crate, which identifies both the following as valid sources:
35///
36/// * `r#"name = "Tom""#`
37/// * `include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/examples/example_config.toml"))`
38///
39/// After expansion, a type `Type` and a static variable `EXPR` holding config data will become available inside the module, i.e.
40///
41/// ```ignore
42/// <VIS> mod <IDENT> {
43/// pub struct Type;
44/// pub static EXPR: Type = Type;
45/// // Other internal implementation details...
46/// }
47/// ```
48///
49/// Users now can freely use `<IDENT>::Type` and `<IDENT>::EXPR`.
50/// Optionally, for convenience, you can export these items directly into the scope via `export` arguments:
51///
52/// ```ignore
53/// #[config(export(type = <TYPE_IDENT>, const = <CONST_IDENT>, static = <STATIC_IDENT>))]
54/// ```
55///
56/// Each will yield a corresponding item:
57///
58/// | Argument | Generated item |
59/// | --- | --- |
60/// | `type = <TYPE_IDENT>` | `pub type <TYPE_IDENT> = <IDENT>::Type;` |
61/// | `const = <CONST_IDENT>` | `pub const <CONST_IDENT>: <IDENT>::Type = <IDENT>::EXPR;` |
62/// | `static = <STATIC_IDENT>` | `pub static <STATIC_IDENT>: <IDENT>::Type = <IDENT>::EXPR;` |
63///
64/// [`proc_macro_expand`]: https://github.com/rust-lang/rust/issues/90765
65/// [`macro_string`]: https://docs.rs/macro-string/latest/macro_string/
66#[proc_macro_attribute]
67pub fn config(
68 input: proc_macro::TokenStream,
69 item: proc_macro::TokenStream,
70) -> proc_macro::TokenStream {
71 emit_tokens_or_error(
72 syn::parse(input)
73 .and_then(|input| syn::parse(item).and_then(|item| config::config(input, item))),
74 )
75}
76
77/// Defines a data structure that can be converted directly from a compatible container.
78///
79/// One needs to ensure all field types, if containing custom types, shall inherit [`ConfigData`] as well.
80/// Use structs with unnamed fields to access from arrays; use structs with named fields to access from tables.
81/// The fields do not necessarily need to be "full" - it may only contain a subset of fields in source data.
82///
83/// To avoid non-identifier key names occurred in source config (e.g. contains `-`), use `#[config_data(rename = "...")]` on certain fields.
84///
85/// ```
86/// use inline_config::ConfigData;
87///
88/// #[derive(ConfigData)]
89/// struct MyStruct {
90/// name: String, // matches "name"
91/// #[config_data(rename = "date-of-birth")]
92/// date_of_birth: String, // matches "date-of-birth"
93/// r#mod: String, // matches "mod"
94/// }
95/// ```
96#[proc_macro_derive(ConfigData, attributes(config_data))]
97pub fn config_data(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
98 emit_tokens_or_error(syn::parse(item).and_then(config_data::config_data))
99}
100
101/// Constructs a path with which one accesses a nested-in piece of data from config.
102///
103/// A path can be constructed by a sequence of keys, separated by `.`, i.e.
104///
105/// ```ignore
106/// path!(<KEY>.<KEY>.<KEY>)
107/// ```
108///
109/// Every `<KEY>` can be either an index (access an array field) or a name (access a table field).
110/// The name may be quoted if it is not a valid identifier (e.g. contains `-`).
111/// The following are all valid keys:
112///
113/// * `key`
114/// * `"key"`
115/// * `"key-in-kebab-case"`
116/// * `0`
117#[proc_macro]
118pub fn path(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
119 emit_tokens_or_error(syn::parse(input).map(path::Path::expr))
120}
121
122/// The type version of [`path!()`]. Used in type bounds.
123#[proc_macro]
124#[allow(non_snake_case)]
125pub fn Path(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
126 emit_tokens_or_error(syn::parse(input).map(path::Path::ty))
127}