Skip to main content

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 format;
7mod from_config;
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/// Attaches config data to a unit struct.
20///
21/// ### Attribute `format`
22///
23/// ```ignore
24/// #[config(format = "toml")]
25/// ```
26///
27/// All sources within a config type can only share the same format.
28/// The format may be omitted if it is clear from extensions of included paths.
29///
30/// Each format has a corresponding feature gate.
31///
32/// ### Attribute `src`
33///
34/// Config sources come in three flavors:
35///
36/// ```ignore
37/// #[config(src = "<SRC_LITERAL>")]
38/// #[config(src = include!("<PATH_LITERAL>"))]
39/// #[config(src = include_env!("<PATH_LITERAL>"))]
40/// ```
41///
42/// There can be an arbitrary number of sources, combined in arbitrary order, as long as they agree on the same format.
43/// When there are multiple sources, they got merged recursively per field, with latter ones overwriting former ones.
44///
45/// When including files, the paths are resolved relative to the call site file.
46/// `include_env!` specially supports environment variable interpolation -
47/// environment variables of form `$ENV_VAR` are interpolated. Escape `$` with `$$`.
48/// The support of environment variable interpolation is to aid any code analyzer to locate files,
49/// as environment variables like `$CARGO_MANIFEST_DIR` and `$OUT_DIR` resolve to absolute paths.
50/// This is mostly inspired by [include_dir](https://docs.rs/include_dir/latest/include_dir/) crate.
51#[proc_macro_derive(Config, attributes(config))]
52pub fn config(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
53    emit_tokens_or_error(syn::parse(item).and_then(config::config))
54}
55
56/// Defines a data structure that can be converted directly from a compatible container.
57///
58/// One needs to ensure all field types, if containing custom types, shall inherit [`FromConfig`] as well.
59/// Use structs with unnamed fields to access from arrays; use structs with named fields to access from tables.
60/// The fields do not necessarily need to be "full" - it may only contain a subset of fields in source data.
61///
62/// To avoid non-identifier key names occurred in source config (e.g. contains `-`), use `#[config(name = "...")]` on certain named fields.
63/// Symmetrically, on unnamed fields you may want to use `#[config(index = ...)]` to remap array indices.
64///
65/// ```
66/// use inline_config::FromConfig;
67///
68/// #[derive(FromConfig)]
69/// struct MyStruct {
70///     name: String, // matches "name"
71///     #[config(name = "date-of-birth")]
72///     date_of_birth: String, // matches "date-of-birth"
73///     r#mod: String, // matches "mod"
74/// }
75/// ```
76#[proc_macro_derive(FromConfig, attributes(config))]
77pub fn from_config(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
78    emit_tokens_or_error(syn::parse(item).and_then(from_config::from_config))
79}
80
81/// Constructs a path with which one accesses a nested-in piece of data from config.
82///
83/// A path can be constructed by a sequence of keys, separated by `.`, i.e.
84///
85/// ```ignore
86/// path!(<KEY>.<KEY>.<KEY>)
87/// ```
88///
89/// Every `<KEY>` can be either an index (access an array field) or a name (access a table field).
90/// The name may be quoted if it is not a valid identifier (e.g. contains `-`).
91/// The following are all valid keys:
92///
93/// * `key`
94/// * `"key"`
95/// * `"key-in-kebab-case"`
96/// * `0`
97#[proc_macro]
98pub fn path(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
99    emit_tokens_or_error(syn::parse(input).map(path::Path::expr))
100}
101
102/// The type version of [`path!()`]. Used in type bounds.
103#[proc_macro]
104#[allow(non_snake_case)]
105pub fn Path(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
106    emit_tokens_or_error(syn::parse(input).map(path::Path::ty))
107}