inline_config/lib.rs
1//! Effortlessly embed config as static data and access with any compatible data structures.
2//!
3//! ## Example
4//!
5//! Below is a basic example illustrating how to declare a static config item and access data from it.
6//!
7//! ```
8//! use inline_config::{Get, config, path};
9//!
10//! config! {
11//! // Note, this looks like a typical static item declaration, but the type is omitted.
12//! // `#[toml]` is needed to specify the format of this source.
13//! // Including a file from disk is also possible, see `examples/include.rs`
14//! pub static MY_CONFIG = #[toml] r#"
15//! title = "TOML example"
16//!
17//! [server]
18//! owner = "Tom"
19//! timeout = 2000
20//! ports = [ 8000, 8001, 8002 ]
21//! "# + #[toml] r#"
22//! [server]
23//! timeout = 5000
24//! "#;
25//! }
26//!
27//! // Multiple types may be compatible. As a cost, type annotation is always required.
28//! let title: &str = MY_CONFIG.get(path!(title));
29//! assert_eq!("TOML example", title);
30//! let title: String = MY_CONFIG.get(path!(title));
31//! assert_eq!("TOML example", title);
32//!
33//! // A deeper path.
34//! let owner: &str = MY_CONFIG.get(path!(server.owner));
35//! assert_eq!("Tom", owner);
36//!
37//! // Any numerical types.
38//! let timeout: u32 = MY_CONFIG.get(path!(server.timeout));
39//! assert_eq!(5000, timeout);
40//! let timeout: f32 = MY_CONFIG.get(path!(server.timeout));
41//!
42//! // A homogeneous array can be accessed as `Vec<T>`.
43//! let ports: Vec<u64> = MY_CONFIG.get(path!(server.ports));
44//! assert_eq!([8000, 8001, 8002].to_vec(), ports);
45//! ```
46//!
47//! See [`config`] and [`path`] for specs on those macros.
48//!
49//! ## Feature flags
50//!
51//! * `json` - supports JSON file format. Enabled by default.
52//! * `yaml` - supports YAML file format. Enabled by default.
53//! * `toml` - supports TOML file format. Enabled by default.
54//! * `indexmap` - enables preserving orders of tables.
55//!
56//! ## Compatible types
57//!
58//! Internally, data from config sources are parsed into one of the seven variants:
59//! null, booleans, integers, floats, strings, arrays, tables.
60//! Each of them has a specific storage representation, and have different compatible types.
61//!
62//! | Representation variant | Storage | Compatible types |
63//! |---|---|---|
64//! | Null | `()` | (See [option types](#option-types)) |
65//! | Boolean | `bool` | `bool` |
66//! | Integer | `i64` | `i8`, `i16`, `i32`, `i64`, `i128`, `isize`,<br>`u8`, `u16`, `u32`, `u64`, `u128`,<br>`usize`, `f32`, `f64` |
67//! | Float | `f64` | `i8`, `i16`, `i32`, `i64`, `i128`, `isize`,<br>`u8`, `u16`, `u32`, `u64`, `u128`,<br>`usize`, `f32`, `f64` |
68//! | String | `&'static str` | `&str`, `String` |
69//! | Array | Structs with unnamed fields | `Vec<T>` if homogeneous,<br>User-defined structs with unnamed fields |
70//! | Table | Structs with named fields | `BTreeMap<&str, T>` if homogeneous,<br>`BTreeMap<String, T>` if homogeneous,<br>`IndexMap<&str, T>` if homogeneous*,<br>`IndexMap<String, T>` if homogeneous*,<br>User-defined structs with named fields |
71//!
72//! * Only available when enabling `indexmap` feature flag.
73//!
74//! ### Container types
75//!
76//! Arrays and tables are both "containers" in the sense of containing children data, therefore you can use [`path`] to access children data.
77//! The only difference between the two containers is that arrays have unnamed but ordered fields, while tables have named but unamed fields.
78//! This suggests you should use indices when accessing a field of an array, but use names when accessing a field of a table.
79//!
80//! Note that they are inhomogeneous in general (children are of different types).
81//! You need to define custom types and derive [`ConfigData`] if you want to access structured data.
82//! Define structs with unnamed fields to model an array, while structs with named fields to model a table.
83//! Specially, in the case when they do contain homogeneous data,
84//! arrays can be accessed as `Vec<T>`, and tables can be accessed as `BTreeMap<&str, T>` or `BTreeMap<String, T>`,
85//! as long as the representation of children can be accessed as `T`.
86//! For containers, this type compatibility comes with a recursive sense.
87//! There's a relevant concept from functional programming, known as [transmogrifying](https://docs.rs/frunk/0.4.4/frunk/#transmogrifying).
88//!
89//! ### Option types
90//!
91//! Some of config formats support null value.
92//! We cannot directly store it as `Option<T>`, as we are not able to tell what `T` is by looking at a literal null.
93//! The following behaviors are implemented.
94//!
95//! * When requesting `Option<T>` from null, return `None`;
96//! * When requesting `T` from null, return `T::default()` if `T: Default`.
97//!
98//! For consistency, you can also request `Option<T>` from a non-null value as long as `T` can be accessed from it,
99//! and the result will be additionally wrapped by a `Some`.
100
101mod convert;
102mod get;
103mod key;
104
105/// Declares static variables containing config data.
106///
107/// A config block may contain any number of config items, in the form illustrated below:
108///
109/// ```ignore
110/// config! {
111/// /// Optional documents
112/// #[optional_attributes]
113/// <VIS> static <IDENT> = <SRC>;
114///
115/// /// Optional documents
116/// #[optional_attributes]
117/// <VIS> static <IDENT> = <SRC> + <SRC> + <SRC>;
118/// }
119/// ```
120///
121/// Each declaration is simply a typical static item, except that the type is omitted.
122///
123/// The expression part looks like a sum of sources.
124/// This is where overwriting takes place. All variants are completely overwritten except for tables, which got merged recursively.
125/// Every `<SRC>` takes one of the following forms:
126///
127/// * `#[toml] r#"name = "Tom""#` - an inline literal config. We require an attribute to specify the format of config.
128/// * `include_config!("example_config.toml")` - a file inclusion. The path is resolved relative to the current file (similar to [`include_str!()`]). The format specifier attribute may be omitted if it's clear from the file extension.
129/// * `include_config_env!("$CARGO_MANIFEST_DIR/examples/example_config.toml")` - also a file inclusion, but environment variables of form `$ENV_VAR` are interpolated. Escape `$` with `$$`.
130///
131/// The support of environment variable interpolation is to aid any code analyzer to locate files,
132/// as environment variables like `$CARGO_MANIFEST_DIR` and `$OUT_DIR` resolve to absolute paths.
133/// This is mostly inspired by [include_dir](https://docs.rs/include_dir/latest/include_dir/) crate.
134pub use inline_config_macros::config;
135
136/// Constructs a path with which one accesses a nested-in piece of data from config.
137///
138/// A path can be constructed by a sequence of keys, separated by `.`.
139/// A key can be either an index (access an array field) or a name (access a table field).
140/// The name may be quoted if it is not a valid identifier (e.g. contains `-`).
141pub use inline_config_macros::path;
142
143/// The type version of [`path`]. Used in type parameters of [`Get`].
144pub use inline_config_macros::Path;
145
146/// Defines a data structure that can be converted directly from a compatible container.
147///
148/// One needs to ensure all field types, if containing custom types, shall inherit [`ConfigData`] as well.
149/// Use structs with unnamed fields to access from arrays; use structs with named fields to access from tables.
150/// The fields do not necessarily need to be "full" - it may only contain a subset of fields in source data.
151///
152/// To avoid non-identifier key names occurred in source config (e.g. contains `-`), use `#[config_data(rename = "...")]` on certain fields.
153///
154/// ```
155/// use inline_config::ConfigData;
156///
157/// #[derive(ConfigData)]
158/// struct MyStruct {
159/// name: String, // matches "name"
160/// #[config_data(rename = "date-of-birth")]
161/// date_of_birth: String, // matches "date-of-birth"
162/// r#mod: String, // matches "mod"
163/// }
164/// ```
165pub use inline_config_macros::ConfigData;
166
167pub use get::Get;
168
169#[doc(hidden)]
170pub mod __private {
171 pub use crate::convert::*;
172 pub use crate::key::*;
173}