configuration/lib.rs
1//! Means of configuration.
2
3/// Define a configuration.
4///
5/// ```
6/// use std::net::SocketAddr;
7/// use std::path::{Path, PathBuf};
8///
9/// configuration::define! {
10/// /// A config.
11/// Config {
12/// /// The foo config.
13/// foo: Foo
14/// where
15/// /// A foo config.
16/// Foo {
17/// /// The bar.
18/// bar: String = [CONFIG_FOO_BAR],
19/// /// The buz.
20/// buz: PathBuf = [
21/// CONFIG_FOO_BUZ,
22/// config_foo_buz() { "buz".into() }
23/// ],
24/// /// The qux.
25/// qux: usize = [
26/// CONFIG_FOO_QUX,
27/// config_foo_qux() { 42 }
28/// ],
29/// },
30/// /// The bar config.
31/// bar: Bar
32/// where
33/// /// A bar config.
34/// Bar {
35/// /// The buz.
36/// buz: String = [CONFIG_BAR_BUZ],
37/// },
38/// /// The buz config.
39/// buz: Buz
40/// where
41/// /// A buz config.
42/// Buz {
43/// /// The qux.
44/// qux: SocketAddr = [
45/// CONFIG_BUZ_QUX,
46/// config_buz_qux() { "0.0.0.0:80".parse().unwrap() }
47/// ],
48/// },
49/// /// The qux.
50/// qux: String = [CONFIG_QUX],
51/// }
52/// }
53///
54/// std::env::set_var("CONFIG_FOO_BAR", "bar");
55/// std::env::set_var("CONFIG_BAR_BUZ", "buz");
56/// std::env::set_var("CONFIG_BUZ_QUX", "127.0.0.1:80");
57/// std::env::set_var("CONFIG_QUX", "qux");
58///
59/// let config = Config::default();
60/// assert_eq!(config.foo.bar, "");
61/// assert_eq!(config.buz.qux, "0.0.0.0:80".parse().unwrap());
62/// assert_eq!(config.qux, "");
63///
64/// let config = Config::try_default().unwrap();
65/// assert_eq!(config.foo.bar, "bar");
66/// assert_eq!(config.foo.buz, Path::new("buz"));
67/// assert_eq!(config.foo.qux, 42);
68/// assert_eq!(config.bar.buz, "buz");
69/// assert_eq!(config.buz.qux, "127.0.0.1:80".parse().unwrap());
70/// assert_eq!(config.qux, "qux");
71/// ```
72#[macro_export]
73macro_rules! define {
74 (
75 $(#[$struct_attribute:meta])*
76 $struct:ident {
77 $(
78 $(#[$field_attribute:meta])*
79 $field:ident:
80 $type:ty
81 $(
82 where
83 $(#[$type_attribute:meta])*
84 $inner:ident {
85 $($nested:tt)+
86 }
87 )?
88 $(= [
89 $default_variable:ident $(,)?
90 $(
91 $default_function:ident ()
92 $default_block:block
93 )?
94 ])?,
95 )+
96 }
97 ) => (
98 $(#[$struct_attribute])*
99 #[derive(Clone, Debug)]
100 pub struct $struct {
101 $(
102 $(#[$field_attribute])*
103 pub $field: $type,
104 )+
105 }
106
107 impl $struct {
108 /// Create an instance from the environment.
109 pub fn try_default() -> std::io::Result<Self> {
110 Ok(Self {
111 $(
112 $field: $crate::define!(
113 @try_default
114 $type [$($inner)?] $($default_variable $($default_function())?)?
115 ),
116 )+
117 })
118 }
119 }
120
121 impl Default for $struct {
122 fn default() -> Self {
123 Self {
124 $(
125 $field: $crate::define!(
126 @default
127 $type [$($inner)?] $($default_variable $($default_function())?)?
128 ),
129 )+
130 }
131 }
132 }
133
134 $(
135 $(
136 $(
137 #[inline]
138 fn $default_function() -> $type $default_block
139 )?
140 )?
141 )+
142
143 $(
144 $(
145 $crate::define! {
146 $(#[$type_attribute])*
147 $inner {
148 $($nested)+
149 }
150 }
151 )?
152 )+
153 );
154 (@default $type:ty []) => (
155 <$type>::default()
156 );
157 (@default $type:ty [$inner:ident]) => (
158 <$type>::default()
159 );
160 (@default $type:ty [$($inner:ident)?] $variable:ident) => (
161 <$type>::default()
162 );
163 (@default $type:ty [$($inner:ident)?] $variable:ident $function:ident ()) => (
164 $function()
165 );
166 (@default $type:ty [$($inner:ident)?] $function:ident ()) => (
167 $function()
168 );
169 (@try_default $type:ty []) => (
170 <$type>::default()
171 );
172 (@try_default $type:ty [$inner:ident]) => (
173 <$type>::try_default()?
174 );
175 (@try_default $type:ty [$($inner:ident)?] $variable:ident) => (
176 match std::env::var(stringify!($variable)) {
177 Ok(value) => std::str::FromStr::from_str(&value).map_err(|error| std::io::Error::other(error))?,
178 _ => <$type>::default(),
179 }
180 );
181 (@try_default $type:ty [$($inner:ident)?] $variable:ident $function:ident ()) => (
182 match std::env::var(stringify!($variable)) {
183 Ok(value) => std::str::FromStr::from_str(&value).map_err(|error| std::io::Error::other(error))?,
184 _ => $function(),
185 }
186 );
187 (@try_default $type:ty [$($inner:ident)?] $function:ident ()) => (
188 $function()
189 );
190}