itconfig_macro/
lib.rs

1#![recursion_limit = "256"]
2#![deny(clippy::all)]
3#![forbid(unsafe_code)]
4#![forbid(non_ascii_idents)]
5
6mod ast;
7mod expand;
8mod parse;
9mod utils;
10
11extern crate proc_macro;
12extern crate proc_macro2;
13
14use self::proc_macro::TokenStream;
15use ast::RootNamespace;
16use quote::ToTokens;
17use syn::parse_macro_input;
18
19/// ### _This API requires the following crate features to be activated: `macro`_
20///
21/// Creates new public mod with function fo get each environment variable of mapping.
22///
23/// All variables are required and program will panic if some variables haven't value, but you
24/// can add default value for specific variable.
25///
26/// Starts with v0.6.0 if you don't have an optional variable, the variable is set automatically.
27///
28/// Example usage
29/// -------------
30///
31/// ```rust
32/// # use itconfig::config;
33/// # use std::env;
34/// config! {
35///     DATABASE_URL: String,
36/// }
37///
38/// # fn main() {
39/// #     env::set_var("DATABASE_URL", "postgres://u:p@localhost:5432/db");
40/// #     config::init();
41/// # }
42/// ```
43///
44/// Config with default value
45///
46/// ```rust
47/// # use itconfig::config;
48/// # use std::env;
49/// config! {
50///     DATABASE_URL: String,
51///     HOST: String => "127.0.0.1",
52/// }
53/// # fn main() {
54/// #     env::set_var("DATABASE_URL", "postgres://u:p@localhost:5432/db");
55/// #     config::init();
56/// # }
57/// ```
58///
59/// By default itconfig lib creates module with 'config' name. But you can use simple meta instruction
60/// if you want to rename module. In the example below we renamed module to 'configuration'
61///
62/// ```rust
63/// # use itconfig::config;
64/// # use std::env;
65/// config! {
66///     #![config(name = "configuration")]
67///
68///     DEBUG: bool,
69/// }
70///
71/// fn main() {
72///     env::set_var("DEBUG", "t");
73///
74///     configuration::init();
75///     assert_eq!(configuration::DEBUG(), true);
76/// }
77/// ```
78///
79/// You also unwrap first config module
80///
81/// ```rust
82/// # use itconfig::config;
83/// # use std::env;
84///
85/// config! {
86///     #![config(unwrap)]
87///
88///     DEBUG: bool,
89/// }
90///
91/// fn main() {
92///     env::set_var("DEBUG", "t");
93///
94///     init();
95///     assert_eq!(DEBUG(), true);
96/// }
97/// ```
98///
99///
100/// Namespaces
101/// ----------
102///
103/// You can use namespaces for env variables
104///
105/// ```rust
106/// # use itconfig::config;
107/// # use std::env;
108///
109/// config! {
110///     DEBUG: bool,
111///     DATABASE {
112///         USERNAME: String,
113///         PASSWORD: String,
114///         HOST: String,
115///     }
116/// }
117/// fn main() {
118///     env::set_var("DEBUG", "t");
119///     env::set_var("DATABASE_USERNAME", "user");
120///     env::set_var("DATABASE_PASSWORD", "pass");
121///     env::set_var("DATABASE_HOST", "localhost");
122///
123///     config::init();
124/// }
125/// ```
126///
127/// Now you can use nested structure in namespaces without limits :)
128///
129/// ```rust
130/// # use itconfig::config;
131/// config! {
132///     FIRST {
133///         SECOND {
134///             THIRD {
135///                 FOO: bool => true,
136///             }
137///         }
138///     }
139/// }
140/// # fn main() { config::init () }
141/// ```
142///
143/// Namespaces supports custom meta
144///
145/// ```rust
146/// # use itconfig::config;
147/// config! {
148///     #[cfg(feature = "first")]
149///     FIRST {
150///         #[cfg(feature = "second")]
151///         SECOND {
152///             #[cfg(feature = "third")]
153///             THIRD {
154///                 FOO: bool => true,
155///             }
156///         }
157///     }
158/// }
159/// # fn main() { config::init () }
160/// ```
161///
162/// Meta
163/// ----
164///
165/// If you want to read custom env name for variable you can change it manually.
166///
167/// **A variable in the nameespace will lose environment prefix**
168///
169/// ```rust
170/// # use itconfig::config;
171/// # use std::env;
172///
173/// config! {
174///     #[env_name = "MY_CUSTOM_NAME"]
175///     PER_PAGE: i32,
176///
177///     APP {
178///         #[env_name = "MY_CUSTOM_NAME"]
179///         RECIPES_PER_PAGE: i32,
180///     }
181/// }
182///
183/// fn main() {
184///     env::set_var("MY_CUSTOM_NAME", "95");
185///
186///     config::init();
187///     assert_eq!(config::PER_PAGE(), 95);
188///     assert_eq!(config::APP::RECIPES_PER_PAGE(), 95);
189/// }
190/// ```
191///
192/// Also you can add custom meta for each variable. For example feature configurations.
193///
194/// ```rust
195/// # use itconfig::config;
196/// config! {
197///     #[cfg(feature = "postgres")]
198///     DATABASE_URL: String,
199///
200///     #[cfg(not(feature = "postgres"))]
201///     DATABASE_URL: String,
202/// }
203/// # fn main() { }
204/// ```
205///
206/// Concatenate
207/// -----------
208///
209/// Try to concatenate env variable or strings or both to you env variable. It's easy!
210///
211/// ```rust
212/// # use itconfig::config;
213/// # use std::env;
214/// config! {
215///     DATABASE_URL < (
216///         "postgres://",
217///         POSTGRES_USERNAME,
218///         ":",
219///         POSTGRES_PASSWORD,
220///         "@",
221///         POSTGRES_HOST => "localhost:5432",
222///         "/",
223///         POSTGRES_DB => "test",
224///     ),
225/// }
226///
227/// fn main() {
228///     env::set_var("POSTGRES_USERNAME", "user");
229///     env::set_var("POSTGRES_PASSWORD", "pass");
230///
231///     config::init();
232///     assert_eq!(config::DATABASE_URL(), "postgres://user:pass@localhost:5432/test".to_string());
233/// }
234/// ```
235///
236/// Concatinated variables can be only strings and support all features like namespaces and meta.
237///
238/// ```rust
239/// # use itconfig::config;
240/// config! {
241///     CONCATED_NAMESPACE {
242///         #[env_name = "DATABASE_URL"]
243///         CONCAT_ENVVAR < (
244///             "postgres://",
245///             NOT_DEFINED_PG_USERNAME => "user",
246///             ":",
247///             NOT_DEFINED_PG_PASSWORD => "pass",
248///             "@",
249///             NOT_DEFINED_PG_HOST => "localhost:5432",
250///             "/",
251///             NOT_DEFINED_PG_DB => "test",
252///         ),
253///     }
254/// }
255/// # fn main() { config::init () }
256/// ```
257///
258/// Static
259/// ------
260///
261/// Starting with 0.11 version you can use lazy static for improve speed of variable. This is very
262/// useful, if you use variable more than once.
263///
264/// ```rust
265/// # use itconfig::config;
266/// # use std::env;
267/// config! {
268///     static APP_BASE_URL => "/api",
269/// }
270///
271/// fn main () {
272///     env::set_var("APP_BASE_URL", "/api/v1");
273///
274///     config::init();
275///     assert_eq!(config::APP_BASE_URL(), "/api/v1");
276/// }
277/// ```
278///
279/// You also can use static with concat variables
280///
281/// ```rust
282/// # use itconfig::config;
283/// config! {
284///     static CONNECTION_STRING < (
285///         "postgres://",
286///         NOT_DEFINED_PG_USERNAME => "user",
287///         ":",
288///         NOT_DEFINED_PG_PASSWORD => "pass",
289///         "@",
290///         NOT_DEFINED_PG_HOST => "localhost:5432",
291///         "/",
292///         NOT_DEFINED_PG_DB => "test",
293///     ),
294/// }
295///
296/// fn main () {
297///     config::init();
298///     assert_eq!(config::CONNECTION_STRING(), "postgres://user:pass@localhost:5432/test".to_string());
299/// }
300/// ```
301///
302///
303/// ---
304///
305/// This module will also contain helper method:
306/// --------------------------------------------
307///
308/// ```rust
309/// pub fn init() {}
310/// ```
311///
312/// Run this at the main function for check all required variables without default value.
313///
314/// Panics
315/// ------
316///
317/// If you miss some required variables your application will panic at startup.
318///
319/// Examples
320/// --------
321///
322/// ```rust
323/// # use itconfig::config;
324/// // use dotenv::dotenv;
325///
326/// config! {
327///     DEBUG: bool => true,
328///     HOST: String => "127.0.0.1",
329/// }
330///
331/// fn main () {
332///     // dotenv().ok();
333///     config::init();
334///     assert_eq!(config::HOST(), String::from("127.0.0.1"));
335/// }
336/// ```
337///
338#[proc_macro]
339pub fn config(input: TokenStream) -> TokenStream {
340    let namespace = parse_macro_input!(input as RootNamespace);
341    namespace.into_token_stream().into()
342}