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_impl_or_error<T: quote::ToTokens>(result: syn::Result<T>) -> proc_macro::TokenStream {
12 match result {
13 Ok(output) => output.into_token_stream().into(),
14 Err(e) => proc_macro_error::abort!(e.span(), e),
15 }
16}
17
18/// Declares a config module.
19///
20/// ```ignore
21/// #[config]
22/// <VIS> mod <IDENT> {
23/// <FORMAT>!(<SRC>);
24/// <FORMAT>!(<SRC>);
25/// <FORMAT>!(<SRC>);
26/// }
27/// ```
28/// When there are multiple sources, they got merged recursively per field, with latter ones overwriting former ones.
29/// All null values need to be overwritten eventually.
30///
31/// Every `<SRC>` shall be a literal string, or a macro invocation expanding into a literal string.
32/// The full support of eager expansion is impossible without nightly-only feature [`proc_macro_expand`].
33/// A subset of eager expansion for built-in macros is handled by [`macro_string`] crate, which identifies both the following as valid sources:
34///
35/// * `r#"name = "Tom""#`
36/// * `include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/examples/example_config.toml"))`
37///
38/// After expansion, a type `Type` and a static variable `EXPR` holding config data will become available inside the module, i.e.
39///
40/// ```ignore
41/// <VIS> mod <IDENT> {
42/// pub struct Type;
43/// pub static EXPR: Type = Type;
44/// // Other internal implementation details...
45/// }
46/// ```
47///
48/// Users now can freely use `<IDENT>::Type` and `<IDENT>::EXPR`.
49/// Optionally, for convenience, you can export these items directly into the scope via `export` arguments:
50///
51/// ```ignore
52/// #[config(export(type = <TYPE_IDENT>, const = <CONST_IDENT>, static = <STATIC_IDENT>))]
53/// ```
54///
55/// Each will yield
56///
57/// | Argument | Generated item |
58/// | --- | --- |
59/// | `type = <TYPE_IDENT>` | `pub type <TYPE_IDENT> = <IDENT>::Type;` |
60/// | `const = <CONST_IDENT>` | `pub const <CONST_IDENT>: <IDENT>::Type = <IDENT>::EXPR;` |
61/// | `static = <STATIC_IDENT>` | `pub static <STATIC_IDENT>: <IDENT>::Type = <IDENT>::EXPR;` |
62///
63/// [`proc_macro_expand`]: https://github.com/rust-lang/rust/issues/90765
64/// [`macro_string`]: https://docs.rs/macro-string/latest/macro_string/
65#[proc_macro_error::proc_macro_error]
66#[proc_macro_attribute]
67pub fn config(
68 input: proc_macro::TokenStream,
69 item: proc_macro::TokenStream,
70) -> proc_macro::TokenStream {
71 emit_impl_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_error::proc_macro_error]
97#[proc_macro_derive(ConfigData, attributes(config_data))]
98pub fn config_data(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
99 emit_impl_or_error(syn::parse(item).and_then(config_data::config_data))
100}
101
102/// Constructs a path with which one accesses a nested-in piece of data from config.
103///
104/// A path can be constructed by a sequence of keys, separated by `.`.
105/// A key can be either an index (access an array field) or a name (access a table field).
106/// The name may be quoted if it is not a valid identifier (e.g. contains `-`).
107#[proc_macro_error::proc_macro_error]
108#[proc_macro]
109pub fn path(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
110 emit_impl_or_error(syn::parse(input).map(path::Path::expr))
111}
112
113/// The type version of [`path!()`]. Used in type bounds.
114#[proc_macro_error::proc_macro_error]
115#[proc_macro]
116#[allow(non_snake_case)]
117pub fn Path(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
118 emit_impl_or_error(syn::parse(input).map(path::Path::ty))
119}