Skip to content

Commit 7c91e13

Browse files
committed
Test env prefix.
1 parent 356b257 commit 7c91e13

15 files changed

+330
-52
lines changed

crates/core/src/container.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ impl Container {
324324
}
325325
}
326326

327+
#[cfg(not(feature = "env"))]
328+
pub fn impl_partial_env_values(&self) -> TokenStream {
329+
quote! {}
330+
}
331+
332+
#[cfg(feature = "env")]
327333
pub fn impl_partial_env_values(&self) -> TokenStream {
328334
let inner = match &self.inner {
329335
ContainerInner::NamedStruct { fields } | ContainerInner::UnnamedStruct { fields } => {
@@ -358,11 +364,21 @@ impl Container {
358364

359365
let internal = ImplResult::impl_use_internal(true);
360366

367+
let prefix_fallback = if let Some(env_prefix) = &self.args.env_prefix {
368+
if env_prefix.is_empty() {
369+
panic!("Attribute `env_prefix` cannot be empty.");
370+
}
371+
372+
quote! { prefix.or_else(Some(#env_prefix)) }
373+
} else {
374+
quote! { prefix }
375+
};
376+
361377
quote! {
362-
fn env_values() -> std::result::Result<Option<Self>, schematic::ConfigError> {
378+
fn env_values_with_prefix(prefix: Option<&str>) -> std::result::Result<Option<Self>, schematic::ConfigError> {
363379
#internal
364380

365-
let mut env = EnvManager::default();
381+
let mut env = EnvManager::new(#prefix_fallback);
366382
let mut partial = Self::default();
367383

368384
#inner

crates/core/src/field.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,9 @@ impl Field {
197197
return Some(env_key.to_owned());
198198
}
199199

200-
if let Some(env_prefix) = &self.container_args.env_prefix {
201-
if env_prefix.is_empty() {
202-
panic!("Attribute `env_prefix` cannot be empty.");
203-
}
204-
205-
return Some(format!("{env_prefix}{}", self.get_name()).to_uppercase());
200+
// When the container has a prefix, we use the field name as a key
201+
if self.container_args.env_prefix.is_some() {
202+
return Some(self.get_name().to_uppercase());
206203
}
207204

208205
None

crates/core/src/field_value.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,18 @@ impl FieldValue {
206206
res.value = if let Some(nested_ident) = &self.nested_ident {
207207
let ident = format_ident!("Partial{}", nested_ident);
208208

209-
quote! {
210-
env.nested(#ident::env_values()?)?
209+
if let Some(env_prefix) = &field_args.env_prefix {
210+
if env_prefix.is_empty() {
211+
panic!("Attribute `env_prefix` cannot be empty.");
212+
}
213+
214+
quote! {
215+
env.nested(#ident::env_values_with_prefix(Some(#env_prefix))?)?
216+
}
217+
} else {
218+
quote! {
219+
env.nested(#ident::env_values()?)?
220+
}
211221
}
212222
} else if let Some(parse_env) = &field_args.parse_env {
213223
quote! {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
mod utils;
2+
3+
use schematic_core::container::Container;
4+
use starbase_sandbox::assert_snapshot;
5+
use syn::parse_quote;
6+
use utils::pretty;
7+
8+
mod container_env {
9+
use super::*;
10+
11+
#[test]
12+
fn can_set_vars() {
13+
let container = Container::from(parse_quote! {
14+
#[derive(Config)]
15+
struct Example {
16+
no_var: String,
17+
no_var_nested: NestedConfig,
18+
#[setting(env = "STR")]
19+
a: String,
20+
#[setting(env = "BOOL")]
21+
b: bool,
22+
#[setting(env = "INT")]
23+
c: usize,
24+
#[setting(nested)]
25+
d: NestedConfig,
26+
#[setting(nested = CustomConfig)]
27+
e: CustomConfig,
28+
}
29+
});
30+
31+
assert_snapshot!(pretty(container.impl_partial_env_values()));
32+
}
33+
34+
#[test]
35+
fn can_set_prefix() {
36+
let container = Container::from(parse_quote! {
37+
#[derive(Config)]
38+
#[config(env_prefix = "PREFIX_")]
39+
struct Example {
40+
#[setting(env = "OVERRIDE")]
41+
a: String,
42+
b: bool,
43+
c: usize,
44+
#[setting(nested)]
45+
d: NestedConfig,
46+
#[setting(nested = CustomConfig)]
47+
e: CustomConfig,
48+
#[setting(nested, env_prefix = "NESTED_")]
49+
f: NestedConfig,
50+
#[setting(nested = CustomConfig, env_prefix = "NESTED_")]
51+
g: CustomConfig,
52+
}
53+
});
54+
55+
assert_snapshot!(pretty(container.impl_partial_env_values()));
56+
}
57+
}

crates/core/tests/field_env_test.rs

Lines changed: 134 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -170,42 +170,144 @@ mod field_env {
170170
mod field_env_prefix {
171171
use super::*;
172172

173-
#[test]
174-
fn accepts_string() {
175-
let container = Container::from(parse_quote! {
176-
#[derive(Config)]
177-
struct Example {
178-
#[setting(env_prefix = "KEY", nested)]
179-
a: String,
180-
}
181-
});
182-
let field = container.inner.get_fields()[0];
173+
mod named_struct {
174+
use super::*;
183175

184-
assert_eq!(field.args.env_prefix.as_ref().unwrap(), "KEY");
185-
}
176+
#[test]
177+
fn accepts_string() {
178+
let container = Container::from(parse_quote! {
179+
#[derive(Config)]
180+
#[config(env_prefix = "PRE_")]
181+
struct Example {
182+
#[setting(env = "A")]
183+
a: String,
184+
#[setting(env_prefix = "OVERRIDE_", nested)]
185+
b: NestedConfig,
186+
}
187+
});
188+
let fields = container.inner.get_fields();
186189

187-
#[test]
188-
#[should_panic(expected = "Attribute `env_prefix` cannot be empty.")]
189-
fn errors_if_empty() {
190-
Container::from(parse_quote! {
191-
#[derive(Config)]
192-
struct Example {
193-
#[setting(env_prefix = "", nested)]
194-
a: String,
195-
}
196-
});
190+
assert_eq!(fields[0].args.env.as_ref().unwrap(), "A");
191+
assert!(fields[0].args.env_prefix.is_none());
192+
193+
assert!(fields[1].args.env.is_none());
194+
assert_eq!(fields[1].args.env_prefix.as_ref().unwrap(), "OVERRIDE_");
195+
}
196+
197+
#[test]
198+
#[should_panic(expected = "Attribute `env_prefix` cannot be empty.")]
199+
fn errors_if_empty() {
200+
Container::from(parse_quote! {
201+
#[derive(Config)]
202+
struct Example {
203+
#[setting(env_prefix = "", nested)]
204+
a: String,
205+
}
206+
})
207+
.impl_partial_env_values();
208+
}
209+
210+
#[test]
211+
#[should_panic(expected = "Cannot use `env_prefix` without `nested`.")]
212+
fn errors_if_not_nested() {
213+
Container::from(parse_quote! {
214+
#[derive(Config)]
215+
struct Example {
216+
#[setting(env_prefix = "KEY")]
217+
a: String,
218+
}
219+
})
220+
.impl_partial_env_values();
221+
}
222+
223+
#[test]
224+
fn supports_nested() {
225+
let container = Container::from(parse_quote! {
226+
#[derive(Config)]
227+
struct Example {
228+
#[setting(nested)]
229+
a: NestedConfig,
230+
#[setting(nested = CustomConfig)]
231+
b: CustomConfig,
232+
#[setting(nested, env_prefix = "PRE_")]
233+
c: NestedConfig,
234+
#[setting(nested = CustomConfig, env_prefix = "PRE_")]
235+
d: CustomConfig,
236+
}
237+
});
238+
239+
assert_snapshot!(pretty(container.impl_partial_env_values()));
240+
}
197241
}
198242

199-
#[test]
200-
#[should_panic(expected = "Cannot use `env_prefix` without `nested`.")]
201-
fn errors_if_not_nested() {
202-
Container::from(parse_quote! {
203-
#[derive(Config)]
204-
struct Example {
205-
#[setting(env_prefix = "KEY")]
206-
a: String,
207-
}
208-
});
243+
mod unnamed_struct {
244+
use super::*;
245+
246+
#[test]
247+
fn accepts_string() {
248+
let container = Container::from(parse_quote! {
249+
#[derive(Config)]
250+
#[config(env_prefix = "PRE_")]
251+
struct Example(
252+
#[setting(env = "A")]
253+
String,
254+
#[setting(env_prefix = "OVERRIDE_", nested)]
255+
NestedConfig,
256+
);
257+
});
258+
let fields = container.inner.get_fields();
259+
260+
assert_eq!(fields[0].args.env.as_ref().unwrap(), "A");
261+
assert!(fields[0].args.env_prefix.is_none());
262+
263+
assert!(fields[1].args.env.is_none());
264+
assert_eq!(fields[1].args.env_prefix.as_ref().unwrap(), "OVERRIDE_");
265+
}
266+
267+
#[test]
268+
#[should_panic(expected = "Attribute `env_prefix` cannot be empty.")]
269+
fn errors_if_empty() {
270+
Container::from(parse_quote! {
271+
#[derive(Config)]
272+
struct Example(
273+
#[setting(env_prefix = "", nested)]
274+
String,
275+
);
276+
})
277+
.impl_partial_env_values();
278+
}
279+
280+
#[test]
281+
#[should_panic(expected = "Cannot use `env_prefix` without `nested`.")]
282+
fn errors_if_not_nested() {
283+
Container::from(parse_quote! {
284+
#[derive(Config)]
285+
struct Example(
286+
#[setting(env_prefix = "KEY")]
287+
String,
288+
);
289+
})
290+
.impl_partial_env_values();
291+
}
292+
293+
#[test]
294+
fn supports_nested() {
295+
let container = Container::from(parse_quote! {
296+
#[derive(Config)]
297+
struct Example(
298+
#[setting(nested)]
299+
NestedConfig,
300+
#[setting(nested = CustomConfig)]
301+
CustomConfig,
302+
#[setting(nested, env_prefix = "PRE_")]
303+
NestedConfig,
304+
#[setting(nested = CustomConfig, env_prefix = "PRE_")]
305+
CustomConfig,
306+
);
307+
});
308+
309+
assert_snapshot!(pretty(container.impl_partial_env_values()));
310+
}
209311
}
210312
}
211313

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
source: crates/core/tests/container_env_test.rs
3+
expression: pretty(container.impl_partial_env_values())
4+
---
5+
fn env_values_with_prefix(
6+
prefix: Option<&str>,
7+
) -> std::result::Result<Option<Self>, schematic::ConfigError> {
8+
use schematic::internal::*;
9+
let mut env = EnvManager::new(prefix.or_else(Some("PREFIX_")));
10+
let mut partial = Self::default();
11+
partial.a = env.get("OVERRIDE")?;
12+
partial.b = env.get("B")?;
13+
partial.c = env.get("C")?;
14+
partial.d = env.nested(PartialNestedConfig::env_values()?)?;
15+
partial.e = env.nested(PartialCustomConfig::env_values()?)?;
16+
partial.f = env
17+
.nested(PartialNestedConfig::env_values_with_prefix(Some("NESTED_"))?)?;
18+
partial.g = env
19+
.nested(PartialCustomConfig::env_values_with_prefix(Some("NESTED_"))?)?;
20+
Ok(if env.is_empty() { None } else { Some(partial) })
21+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
source: crates/core/tests/container_env_test.rs
3+
expression: pretty(container.impl_partial_env_values())
4+
---
5+
fn env_values_with_prefix(
6+
prefix: Option<&str>,
7+
) -> std::result::Result<Option<Self>, schematic::ConfigError> {
8+
use schematic::internal::*;
9+
let mut env = EnvManager::new(prefix);
10+
let mut partial = Self::default();
11+
partial.a = env.get("STR")?;
12+
partial.b = env.get("BOOL")?;
13+
partial.c = env.get("INT")?;
14+
partial.d = env.nested(PartialNestedConfig::env_values()?)?;
15+
partial.e = env.nested(PartialCustomConfig::env_values()?)?;
16+
Ok(if env.is_empty() { None } else { Some(partial) })
17+
}

crates/core/tests/snapshots/field_env_test__field_env__named_struct__supports_different_types.snap

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
source: crates/core/tests/field_env_test.rs
33
expression: pretty(container.impl_partial_env_values())
44
---
5-
fn env_values() -> std::result::Result<Option<Self>, schematic::ConfigError> {
5+
fn env_values_with_prefix(
6+
prefix: Option<&str>,
7+
) -> std::result::Result<Option<Self>, schematic::ConfigError> {
68
use schematic::internal::*;
7-
let mut env = EnvManager::default();
9+
let mut env = EnvManager::new(prefix);
810
let mut partial = Self::default();
911
partial.a = env.get("A")?;
1012
partial.b = env.get("B")?;

crates/core/tests/snapshots/field_env_test__field_env__named_struct__supports_nested.snap

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
source: crates/core/tests/field_env_test.rs
33
expression: pretty(container.impl_partial_env_values())
44
---
5-
fn env_values() -> std::result::Result<Option<Self>, schematic::ConfigError> {
5+
fn env_values_with_prefix(
6+
prefix: Option<&str>,
7+
) -> std::result::Result<Option<Self>, schematic::ConfigError> {
68
use schematic::internal::*;
7-
let mut env = EnvManager::default();
9+
let mut env = EnvManager::new(prefix);
810
let mut partial = Self::default();
911
partial.a = env.nested(PartialNestedConfig::env_values()?)?;
1012
partial.b = env.nested(PartialCustomConfig::env_values()?)?;

crates/core/tests/snapshots/field_env_test__field_env__unnamed_struct__supports_different_types.snap

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
source: crates/core/tests/field_env_test.rs
33
expression: pretty(container.impl_partial_env_values())
44
---
5-
fn env_values() -> std::result::Result<Option<Self>, schematic::ConfigError> {
5+
fn env_values_with_prefix(
6+
prefix: Option<&str>,
7+
) -> std::result::Result<Option<Self>, schematic::ConfigError> {
68
use schematic::internal::*;
7-
let mut env = EnvManager::default();
9+
let mut env = EnvManager::new(prefix);
810
let mut partial = Self::default();
911
partial.1 = env.get("A")?;
1012
partial.2 = env.get("B")?;

0 commit comments

Comments
 (0)