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}