Skip to main content

dotenvy_derive/
lib.rs

1mod codegen;
2mod parse;
3
4use proc_macro::TokenStream;
5use syn::{DeriveInput, parse_macro_input};
6
7/// Derives struct initialization from `.env` values at compile time.
8///
9/// Each field must carry `#[env("VAR_NAME")]` and be typed `&'static str`.
10/// The consumer crate must also depend on `dotenvy_macro`.
11///
12/// # Modes
13///
14/// ## `impl Default` (default)
15///
16/// ```rust,ignore
17/// #[derive(Bind)]
18/// pub struct MailConfig {
19///     #[env("MAIL_HOST")]
20///     pub host: &'static str,
21///     #[env("MAIL_API_KEY")]
22///     pub api_key: &'static str,
23/// }
24///
25/// let cfg = MailConfig::default();
26/// ```
27///
28/// ## `pub const INSTANCE` (with `#[env_static]`)
29///
30/// Add `#[env_static]` to emit a compile-time constant usable in `const` contexts:
31///
32/// ```rust,ignore
33/// #[derive(Bind)]
34/// #[env_static]
35/// pub struct MailConfig {
36///     #[env("MAIL_HOST")]
37///     pub host: &'static str,
38///     #[env("MAIL_API_KEY")]
39///     pub api_key: &'static str,
40/// }
41///
42/// pub const CONFIG: AppConfig = AppConfig {
43///     mail: MailConfig::INSTANCE,
44/// };
45/// ```
46///
47/// # Errors
48///
49/// Compile error if:
50/// - Applied to an enum, union, tuple struct, or unit struct
51/// - Any field is missing `#[env("VAR_NAME")]`
52/// - `#[env(...)]` argument is not a string literal
53#[proc_macro_derive(Bind, attributes(env, env_static))]
54pub fn derive_bind(input: TokenStream) -> TokenStream {
55    let input = parse_macro_input!(input as DeriveInput);
56
57    let is_static = input.attrs.iter().any(|a| a.path().is_ident("env_static"));
58
59    let bindings = match parse::parse_derive_input(&input) {
60        Ok(b) => b,
61        Err(e) => return e.to_compile_error().into(),
62    };
63
64    if is_static {
65        codegen::emit_static(&input.ident, &bindings).into()
66    } else {
67        codegen::emit_default(&input.ident, &bindings).into()
68    }
69}