inline_config/
lib.rs

1//! Effortlessly embed config modules and access with any compatible types.
2//!
3//! ## Example
4//!
5//! Below is a basic example illustrating how to declare a config module and access data from it.
6//!
7//! ```
8//! use inline_config::{config, path};
9//!
10//! // Declare a config module containing literal sources.
11//! // With `export(static = MY_CONFIG)`, a static variable `MY_CONFIG` will be brought into scope.
12//! #[config(export(static = MY_CONFIG))]
13//! mod my_config {
14//!     // When there are multiple sources, latter ones overwrite former ones.
15//!     // Including a file from disk is also possible, see `examples/include.rs`.
16//!     toml!(
17//!     r#"
18//!         title = "TOML example"
19//!
20//!         [server]
21//!         owner = "Tom"
22//!         timeout = 2000
23//!         ports = [ 8000, 8001, 8002 ]
24//!         "#
25//!     );
26//!     toml!(
27//!         r#"
28//!         [server]
29//!         timeout = 5000
30//!         "#
31//!     );
32//! }
33//!
34//! // Multiple types may implement `From` trait, so type annotations are required.
35//! let title: &str = MY_CONFIG[path!(title)].into();
36//! assert_eq!("TOML example", title);
37//! let title: String = MY_CONFIG[path!(title)].into();
38//! assert_eq!("TOML example", title);
39//!
40//! // A deeper path.
41//! let owner: &str = MY_CONFIG[path!(server.owner)].into();
42//! assert_eq!("Tom", owner);
43//!
44//! // Any numerical types.
45//! let timeout: u32 = MY_CONFIG[path!(server.timeout)].into();
46//! assert_eq!(5000, timeout);
47//! let timeout: f32 = MY_CONFIG[path!(server.timeout)].into();
48//!
49//! // A homogeneous array can be accessed as `Vec<T>`.
50//! let ports: Vec<u64> = MY_CONFIG[path!(server.ports)].into();
51//! assert_eq!([8000, 8001, 8002].to_vec(), ports);
52//! ```
53//!
54//! See [`config`] and [`path!()`] for specs on those macros.
55//!
56//! ## Compatible types
57//!
58//! Internally, data from config sources are parsed into one of the seven variants:
59//! booleans, unsigned integers, signed integers, floats, strings, arrays, tables.
60//! Each of them has a specific storage representation, and have different compatible types.
61//!
62//! | Representation variant | Compatible types |
63//! | --- | --- |
64//! | Boolean | [`bool`] |
65//! | Unsigned Integer | [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`isize`],<br>[`u8`], [`u16`], [`u32`], [`u64`], [`u128`], [`usize`],<br>[`f32`], [`f64`] |
66//! | Signed Integer | [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`isize`],<br>[`f32`], [`f64`] |
67//! | Float | [`f32`], [`f64`] |
68//! | String | [`&str`], [`String`] |
69//! | Array | [`Vec<T>`] if homogeneous,<br>User-defined structs deriving [`ConfigData`] with unnamed fields |
70//! | Table | [`std::collections::BTreeMap<&str, T>`] if homogeneous,<br>[`std::collections::BTreeMap<String, T>`] if homogeneous,<br>[`indexmap::IndexMap<&str, T>`] if homogeneous\*,<br>[`indexmap::IndexMap<String, T>`] if homogeneous\*,<br>User-defined structs deriving [`ConfigData`] 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 [`std::collections::BTreeMap<&str, T>`] or [`std::collections::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].
88//!
89//! [transmogrifying]: https://docs.rs/frunk/0.4.4/frunk/#transmogrifying
90//!
91//! ## Feature flags
92//!
93//! * `json` - supports JSON file format. Enabled by default.
94//! * `yaml` - supports YAML file format. Enabled by default.
95//! * `toml` - supports TOML file format. Enabled by default.
96//! * `indexmap` - enables preserving orders of tables.
97
98pub use inline_config_macros::*;
99
100#[doc(hidden)]
101pub mod __private {
102    use std::marker::PhantomData;
103
104    // Borrowed from `frunk_core::labelled::chars`.
105    pub mod chars {
106        macro_rules! create_enums_for {
107        ($($c:tt)*) => {
108            $(
109                #[allow(non_camel_case_types)]
110                pub struct $c;
111            )*
112        };
113    }
114
115        create_enums_for!(
116            A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
117            a b c d e f g h i j k l m n o p q r s t u v w x y z
118            _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 __
119        );
120
121        // For unicode chars.
122        pub struct UC<const CODEPOINT: u32>;
123    }
124
125    pub struct KeyIndex<Index>(PhantomData<Index>);
126
127    pub struct KeyName<Name>(PhantomData<Name>);
128
129    impl<Index> Default for KeyIndex<Index> {
130        fn default() -> Self {
131            Self(PhantomData)
132        }
133    }
134
135    impl<Name> Default for KeyName<Name> {
136        fn default() -> Self {
137            Self(PhantomData)
138        }
139    }
140
141    #[derive(Default)]
142    pub struct PathNil;
143
144    #[derive(Default)]
145    pub struct PathCons<K, KS>(pub K, pub KS);
146}