1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Derive macros for [`confroid`](https://docs.rs/confroid).
use TokenStream;
use ;
/// Derive `confroid::FromEnv` for a named struct.
///
/// `Config` maps struct fields to environment variables. Field names are
/// converted to `SCREAMING_SNAKE_CASE`, and nested segments are joined with
/// `__`.
///
/// # Example
///
/// ```ignore
/// #[derive(confroid::Config)]
/// #[confroid(prefix = "GUPPY")]
/// struct Config {
/// #[confroid(default = 8080)]
/// port: u16,
/// database: Database,
/// }
///
/// #[derive(confroid::Config)]
/// struct Database {
/// #[confroid(name = "URL")]
/// connection_string: String,
/// }
///
/// // Reads `GUPPY__PORT` and `GUPPY__DATABASE__URL`.
/// let config: Config = confroid::from_env()?;
/// # Ok::<(), confroid::ConfroidError>(())
/// ```
///
/// # Container attributes
///
/// Container attributes are placed on the struct below `#[derive(Config)]`:
///
/// - **`prefix = "..."`** — prepend a segment to every environment variable
/// represented by the struct. For example, `#[confroid(prefix = "GUPPY")]`
/// makes a field named `port` read from `GUPPY__PORT`. Prefixes compose when
/// a prefixed struct is nested inside another config.
///
/// # Field attributes
///
/// Field attributes customize naming, fallback values, parsing, and generated
/// configuration documentation:
///
/// - **`name = "..."`** — override the field's environment-variable segment.
/// On a nested field, the override applies to the entire nested prefix.
/// - **`default`** — use [`Default::default()`] when the variable is absent.
/// - **`default = expression`** — use an explicit fallback when the variable is
/// absent. String literals and compatible expressions are converted with
/// [`Into::into`]. A present value that fails to parse still returns an error.
/// - **`example = expression`** — attach an example value for
/// `confroid::env_example` and `confroid::markdown_table` when the `docs`
/// feature is enabled. It does not affect parsing.
/// - **`auto_vec`** — parse a `Vec<T>` from one delimited variable rather than
/// indexed variables such as `NAMES__0` and `NAMES__1`.
/// - **`auto_vec_delimiter = "..."`** — set the delimiter used by `auto_vec`;
/// the default is `,`.
/// - **`humantime`** — parse a `std::time::Duration` or
/// `Option<std::time::Duration>` using values such as `30s` or `1h 15m`.
/// This requires confroid's `humantime` feature.
///
/// Attributes can be combined:
///
/// ```ignore
/// #[derive(confroid::Config)]
/// struct Config {
/// #[confroid(
/// name = "ALLOWED_HOSTS",
/// auto_vec,
/// auto_vec_delimiter = ";",
/// default
/// )]
/// hosts: Vec<String>,
/// }
/// ```
///
/// # Error reporting
///
/// All fields are evaluated before a failed read returns. Independent failures
/// are flattened into `confroid::ConfroidError::Multiple` in field declaration
/// order, allowing all missing and invalid variables to be fixed in one pass.
/// A single failure is returned as its leaf variant.
///
/// Parser failures are retained as boxed error sources rather than converted to
/// strings. Consequently, custom scalar parse errors must implement
/// `std::error::Error + Send + Sync + 'static`.
///
/// # Supported structs
///
/// The derive supports structs with named fields. Each field's type must
/// implement `confroid::FromEnv`. Optional values, nested configs, vectors, and
/// hash maps use their `FromEnv` implementations to determine presence and
/// deserialize their children.
/// Derive `confroid::FromEnv` for a scalar configuration value.
///
/// `ConfigValue` is the type-definition alternative to
/// `confroid::from_str_scalar!`. The type must implement
/// [`FromStr`](core::str::FromStr), and its parser error must implement
/// [`Error`](std::error::Error), `Send`, `Sync`, and `'static` so it can be
/// retained as the source of `confroid::ConfroidError::EnvVarInvalid`.
///
/// When confroid's `docs` feature is enabled, the type must also implement
/// [`Display`](std::fmt::Display). This explicit generated bound makes missing
/// display support point back to the scalar type rather than to a config field
/// that happens to use `default` or `example`.
///
/// # Example
///
/// ```ignore
/// use std::str::FromStr;
///
/// #[derive(Debug, confroid::ConfigValue)]
/// enum Mode {
/// Standalone,
/// Cluster,
/// }
///
/// impl FromStr for Mode {
/// type Err = std::io::Error;
///
/// fn from_str(value: &str) -> Result<Self, Self::Err> {
/// match value {
/// "standalone" => Ok(Self::Standalone),
/// "cluster" => Ok(Self::Cluster),
/// other => Err(std::io::Error::new(
/// std::io::ErrorKind::InvalidInput,
/// format!("unknown mode `{other}`"),
/// )),
/// }
/// }
/// }
/// ```