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 type containing literal sources.
11//! #[derive(Config)]
12//! // While only using literal sources,
13//! // a format needs to be specified.
14//! // Including a file from disk is also possible,
15//! // see `examples/include.rs`.
16//! #[config(format = "toml")]
17//! // When there are multiple sources,
18//! // latter ones overwrite former ones.
19//! #[config(src = r#"
20//! title = "TOML example"
21//!
22//! [server]
23//! owner = "Tom"
24//! timeout = 2000
25//! ports = [ 8000, 8001, 8002 ]
26//! "#)]
27//! #[config(src = r#"
28//! [server]
29//! timeout = 5000
30//! "#)]
31//! struct MyConfig;
32//!
33//! // Use `Index`, `From` traits to access data.
34//! // Different types may be accessible from a field.
35//! let title: &str = MyConfig[path!(title)].into();
36//! assert_eq!("TOML example", title);
37//! let title: String = MyConfig[path!(title)].into();
38//! assert_eq!("TOML example", title);
39//!
40//! // A deeper path.
41//! let owner: &str = MyConfig[path!(server.owner)].into();
42//! assert_eq!("Tom", owner);
43//!
44//! // Any numerical types.
45//! let timeout: u32 = MyConfig[path!(server.timeout)].into();
46//! assert_eq!(5000, timeout);
47//! let timeout: f32 = MyConfig[path!(server.timeout)].into();
48//!
49//! // A homogeneous array can be accessed as `Vec<T>`.
50//! let ports: Vec<u64> = MyConfig[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 [`FromConfig`] 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 [`FromConfig`] with named fields |
71//!
72//! \* Only available when enabling `indexmap` feature flag.
73//!
74//! [`indexmap::IndexMap<&str, T>`]: https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html
75//! [`indexmap::IndexMap<String, T>`]: https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html
76//!
77//! ### Container types
78//!
79//! Arrays and tables are both "containers" in the sense of containing children data, therefore you can use [`path!()`] to access children data.
80//! The only difference between the two containers is that arrays have unnamed but ordered fields, while tables have named but unamed fields.
81//! This suggests you should use indices when accessing a field of an array, but use names when accessing a field of a table.
82//!
83//! Note that they are inhomogeneous in general (children are of different types).
84//! You need to define custom types and derive [`FromConfig`] if you want to access structured data.
85//! Define structs with unnamed fields to model an array, while structs with named fields to model a table.
86//! Specially, in the case when they do contain homogeneous data,
87//! arrays can be accessed as [`Vec<T>`], and tables can be accessed as [`std::collections::BTreeMap<&str, T>`] or [`std::collections::BTreeMap<String, T>`],
88//! as long as the representation of children can be accessed as `T`.
89//! For containers, this type compatibility comes with a recursive sense.
90//! There's a relevant concept from functional programming, known as [transmogrifying].
91//!
92//! [transmogrifying]: https://docs.rs/frunk/0.4.4/frunk/#transmogrifying
93//!
94//! ## Feature flags
95//!
96//! * `json` - supports JSON file format. Enabled by default.
97//! * `yaml` - supports YAML file format. Enabled by default.
98//! * `toml` - supports TOML file format. Enabled by default.
99//! * `indexmap` - enables preserving orders of tables.
100
101pub use inline_config_macros::*;
102
103#[doc(hidden)]
104pub mod __private {
105 use std::marker::PhantomData;
106
107 // Borrowed from `frunk_core::labelled::chars`.
108 pub mod chars {
109 macro_rules! create_enums_for {
110 ($($ident:ident)*) => {
111 $(
112 #[allow(non_camel_case_types)]
113 pub struct $ident;
114 )*
115 };
116 }
117
118 create_enums_for!(
119 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
120 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
121 _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 __
122 );
123
124 // For all other chars.
125 pub struct Ch<const CHAR: char>;
126 }
127
128 #[derive(Default)]
129 pub struct KeyIndex<const INDEX: usize>;
130
131 pub struct KeyName<Name>(PhantomData<Name>);
132
133 impl<Name> Default for KeyName<Name> {
134 fn default() -> Self {
135 Self(PhantomData)
136 }
137 }
138
139 #[derive(Default)]
140 pub struct PathNil;
141
142 #[derive(Default)]
143 pub struct PathCons<K, P>(K, P);
144}