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}