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}