init4_bin_base/utils/from_env.rs
1use signet_constants::{
2 HostConstants, RollupConstants, SignetConstants, SignetEnvironmentConstants,
3 SignetSystemConstants,
4};
5use std::{convert::Infallible, env::VarError, num::ParseIntError, str::FromStr};
6use tracing_core::metadata::ParseLevelError;
7
8/// The `derive(FromEnv)` macro.
9///
10/// This macro generates a [`FromEnv`] implementation for the struct it is
11/// applied to. It will generate a `from_env` function that loads the struct
12/// from the environment. It will also generate an `inventory` function that
13/// returns a list of all environment variables that are required to load the
14/// struct.
15///
16/// The macro also generates a `____EnvError` type that captures errors that can
17/// occur when trying to create an instance of the struct from environment
18/// variables. This error type is used in the `FromEnv` trait implementation.
19///
20/// ## [`FromEnv`] vs [`FromEnvVar`]
21///
22/// While [`FromEnvVar`] deals with loading simple types from the environment,
23/// [`FromEnv`] is for loading complex types. It builds a struct from the
24/// environment, usually be delegating each field to a [`FromEnvVar`] or
25/// [`FromEnv`] implementation.
26///
27/// When using the derive macro, the props of the struct must implement
28/// [`FromEnv`] or [`FromEnvVar`]. Props that implement [`FromEnv`] contain all
29/// the information needed to load the struct from the environment. Props
30/// that implement [`FromEnvVar`] need additional information via attributes.
31///
32/// ## Attributes
33///
34/// The macro supports the following attributes:
35/// - `var = ""`: The name of the environment variable. **This is required if
36/// the prop implements [`FromEnvVar`] and forbidden if the prop implements
37/// [`FromEnv`].**
38/// - `desc = ""`: A description of the environment variable. **This is required
39/// if the prop implements [`FromEnvVar`] and forbidden if the prop
40/// implements [`FromEnv`].**
41/// - `optional`: Marks the prop as optional. This is currently only used in the
42/// generated `fn inventory`, and is informational.
43/// - `infallible`: Marks the prop as infallible. This means that the prop
44/// cannot fail to be parsed after the environment variable is loaded.
45/// - `skip`: Marks the prop as skipped. This means that the prop will not be
46/// loaded from the environment, and will be generated via
47/// `Default::default()` instead.
48///
49/// ## Conditions of use
50///
51/// There are a few usage requirements:
52///
53/// - Struct props MUST implement either [`FromEnvVar`] or [`FromEnv`].
54/// - If the prop implements [`FromEnvVar`], it must be tagged as follows:
55/// - `var = "ENV_VAR_NAME"`: The environment variable name to load.
56/// - `desc = "description"`: A description of the environment variable.
57/// - If the prop is an [`Option<T>`], it must be tagged as follows:
58/// - `optional`
59/// - If the prop's associated error type is [`Infallible`], it must be tagged
60/// as follows:
61/// - `infallible`
62/// - If used within this crate (`init4_bin_base`), the entire struct must be
63/// tagged with `#[from_env(crate)]` (see the [`SlotCalculator`] for an
64/// example).
65///
66/// # Examples
67///
68/// The following example shows how to use the macro:
69///
70/// ```
71/// # // I am unsure why we need this, as identical code works in
72/// # // integration tests. However, compile test fails without it.
73/// # #![allow(proc_macro_derive_resolution_fallback)]
74/// use init4_bin_base::utils::from_env::{FromEnv};
75///
76/// #[derive(Debug, FromEnv)]
77/// pub struct MyCfg {
78/// #[from_env(var = "COOL_DUDE", desc = "Some u8 we like :o)")]
79/// pub my_cool_u8: u8,
80///
81/// #[from_env(var = "CHUCK", desc = "Charles is a u64")]
82/// pub charles: u64,
83///
84/// #[from_env(
85/// var = "PERFECT",
86/// desc = "A bold and neat string",
87/// infallible,
88/// )]
89/// pub strings_cannot_fail: String,
90///
91/// #[from_env(
92/// var = "MAYBE_NOT_NEEDED",
93/// desc = "This is an optional string",
94/// optional,
95/// infallible,
96/// )]
97/// maybe_not_needed: Option<String>,
98/// }
99///
100/// #[derive(Debug, FromEnv)]
101/// pub struct MyBiggerCfg {
102/// #[from_env(var = "BIGGGG_CONFIGGGG", desc = "A big config", infallible)]
103/// pub big_config: String,
104///
105/// // Note that becuase `MyCfg` implements `FromEnv`, we do not need to
106/// // specify the `var` and `desc` attributes.
107/// pub little_config: MyCfg,
108/// }
109///
110/// // The [`FromEnv`] trait is implemented for the struct, and the struct can
111/// // be loaded from the environment.
112/// # fn use_it() {
113/// if let Err(missing) = MyBiggerCfg::check_inventory() {
114/// println!("Missing environment variables:");
115/// for var in missing {
116/// println!("{}: {}", var.var, var.description);
117/// }
118/// }
119/// # }
120/// ```
121///
122/// This will generate a [`FromEnv`] implementation for the struct, and a
123/// `MyCfgEnvError` type that is used to represent errors that can occur when
124/// loading from the environment. The error generated will look like this:
125///
126/// ```ignore
127/// pub enum MyCfgEnvError {
128/// MyCoolU8(<u8 as FromEnvVar>::Error),
129/// Charles(<u64 as FromEnvVar>::Error),
130/// // No variants for infallible errors.
131/// }
132/// ```
133///
134/// [`Infallible`]: std::convert::Infallible
135/// [`SlotCalculator`]: crate::utils::SlotCalculator
136/// [`FromEnv`]: crate::utils::from_env::FromEnv
137/// [`FromEnvVar`]: crate::utils::from_env::FromEnvVar
138pub use init4_from_env_derive::FromEnv;
139
140/// Details about an environment variable. This is used to generate
141/// documentation for the environment variables and by the [`FromEnv`] trait to
142/// check if necessary environment variables are present.
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub struct EnvItemInfo {
145 /// The environment variable name.
146 pub var: &'static str,
147 /// A description of the environment variable function in the CFG.
148 pub description: &'static str,
149 /// Whether the environment variable is optional or not.
150 pub optional: bool,
151}
152
153/// Error type for loading from the environment. See the [`FromEnv`] trait for
154/// more information.
155#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
156pub enum FromEnvErr<Inner> {
157 /// The environment variable is missing.
158 #[error("error reading variable {0}: {1}")]
159 EnvError(String, VarError),
160 /// The environment variable is empty.
161 #[error("environment variable {0} is empty")]
162 Empty(String),
163 /// The environment variable is present, but the value could not be parsed.
164 #[error("failed to parse environment variable {0}")]
165 ParseError(#[from] Inner),
166}
167
168impl FromEnvErr<Infallible> {
169 /// Convert the error into another error type.
170 pub fn infallible_into<T>(self) -> FromEnvErr<T> {
171 match self {
172 Self::EnvError(s, e) => FromEnvErr::EnvError(s, e),
173 Self::Empty(s) => FromEnvErr::Empty(s),
174 Self::ParseError(_) => unreachable!(),
175 }
176 }
177}
178
179impl<Inner> FromEnvErr<Inner> {
180 /// Create a new error from another error type.
181 pub fn from<Other>(other: FromEnvErr<Other>) -> Self
182 where
183 Inner: From<Other>,
184 {
185 match other {
186 FromEnvErr::EnvError(s, e) => Self::EnvError(s, e),
187 FromEnvErr::Empty(s) => Self::Empty(s),
188 FromEnvErr::ParseError(e) => Self::ParseError(Inner::from(e)),
189 }
190 }
191
192 /// Map the error to another type. This is useful for converting the error
193 /// type to a different type, while keeping the other error information
194 /// intact.
195 pub fn map<New>(self, f: impl FnOnce(Inner) -> New) -> FromEnvErr<New> {
196 match self {
197 Self::EnvError(s, e) => FromEnvErr::EnvError(s, e),
198 Self::Empty(s) => FromEnvErr::Empty(s),
199 Self::ParseError(e) => FromEnvErr::ParseError(f(e)),
200 }
201 }
202
203 /// Missing env var.
204 pub fn env_err(var: &str, e: VarError) -> Self {
205 Self::EnvError(var.to_string(), e)
206 }
207
208 /// Empty env var.
209 pub fn empty(var: &str) -> Self {
210 Self::Empty(var.to_string())
211 }
212
213 /// Error while parsing.
214 pub const fn parse_error(err: Inner) -> Self {
215 Self::ParseError(err)
216 }
217}
218
219/// Convenience function for parsing a value from the environment, if present
220/// and non-empty.
221pub fn parse_env_if_present<T: FromStr>(env_var: &str) -> Result<T, FromEnvErr<T::Err>> {
222 let s = std::env::var(env_var).map_err(|e| FromEnvErr::env_err(env_var, e))?;
223
224 if s.is_empty() {
225 Err(FromEnvErr::empty(env_var))
226 } else {
227 s.parse().map_err(Into::into)
228 }
229}
230
231/// Trait for loading from the environment.
232///
233/// This trait is for structs or other complex objects, that need to be loaded
234/// from the environment. It expects that
235///
236/// - The struct is [`Sized`] and `'static`.
237/// - The struct elements can be parsed from strings.
238/// - Struct elements are at fixed env vars, known by the type at compile time.
239///
240/// As such, unless the env is modified, these are essentially static runtime
241/// values. We do not recommend using dynamic env vars.
242///
243/// ## [`FromEnv`] vs [`FromEnvVar`]
244///
245/// While [`FromEnvVar`] deals with loading simple types from the environment,
246/// [`FromEnv`] is for loading complex types. It builds a struct from the
247/// environment, usually be delegating each field to a [`FromEnvVar`] or
248/// [`FromEnv`] implementation. [`FromEnv`] effectively defines a singleton
249/// configuration object, which is produced by loading many env vars, while
250/// [`FromEnvVar`] defines a procedure for loading data from a single
251/// environment variable.
252///
253/// ## Implementing [`FromEnv`]
254///
255/// Please use the [`FromEnv`](macro@FromEnv) derive macro to implement this
256/// trait.
257///
258/// ## Note on error types
259///
260/// [`FromEnv`] and [`FromEnvVar`] are often deeply nested. This means that
261/// error types are often nested as well. To avoid this, we use a single error
262/// type [`FromEnvVar`] that wraps an inner error type. This allows us to
263/// ensure that env-related errors (e.g. missing env vars) are not lost in the
264/// recursive structure of parsing errors. Environment errors are always at the
265/// top level, and should never be nested. **Do not use [`FromEnvErr<T>`] as
266/// the `Error` associated type in [`FromEnv`].**
267///
268/// ```no_compile
269/// // Do not do this
270/// impl FromEnv for MyType {
271/// type Error = FromEnvErr<MyTypeErr>;
272/// }
273///
274/// // Instead do this:
275/// impl FromEnv for MyType {
276/// type Error = MyTypeErr;
277/// }
278/// ```
279///
280pub trait FromEnv: core::fmt::Debug + Sized + 'static {
281 /// Error type produced when loading from the environment.
282 type Error: core::error::Error + Clone + PartialEq + Eq;
283
284 /// Get the required environment variable names for this type.
285 ///
286 /// ## Note
287 ///
288 /// This MUST include the environment variable names for all fields in the
289 /// struct, including optional vars.
290 fn inventory() -> Vec<&'static EnvItemInfo>;
291
292 /// Get a list of missing environment variables.
293 ///
294 /// This will check all environment variables in the inventory, and return
295 /// a list of those that are non-optional and missing. This is useful for
296 /// reporting missing environment variables.
297 fn check_inventory() -> Result<(), Vec<&'static EnvItemInfo>> {
298 let mut missing = Vec::new();
299 for var in Self::inventory() {
300 if std::env::var(var.var).is_err() && !var.optional {
301 missing.push(var);
302 }
303 }
304 if missing.is_empty() {
305 Ok(())
306 } else {
307 Err(missing)
308 }
309 }
310
311 /// Load from the environment.
312 fn from_env() -> Result<Self, FromEnvErr<Self::Error>>;
313}
314
315impl<T> FromEnv for Option<T>
316where
317 T: FromEnv,
318{
319 type Error = T::Error;
320
321 fn inventory() -> Vec<&'static EnvItemInfo> {
322 T::inventory()
323 }
324
325 fn check_inventory() -> Result<(), Vec<&'static EnvItemInfo>> {
326 T::check_inventory()
327 }
328
329 fn from_env() -> Result<Self, FromEnvErr<Self::Error>> {
330 match T::from_env() {
331 Ok(v) => Ok(Some(v)),
332 Err(FromEnvErr::Empty(_)) | Err(FromEnvErr::EnvError(_, _)) => Ok(None),
333 Err(e) => Err(e),
334 }
335 }
336}
337
338impl<T> FromEnv for Box<T>
339where
340 T: FromEnv,
341{
342 type Error = T::Error;
343
344 fn inventory() -> Vec<&'static EnvItemInfo> {
345 T::inventory()
346 }
347
348 fn check_inventory() -> Result<(), Vec<&'static EnvItemInfo>> {
349 T::check_inventory()
350 }
351
352 fn from_env() -> Result<Self, FromEnvErr<Self::Error>> {
353 T::from_env().map(Box::new)
354 }
355}
356
357impl<T> FromEnv for std::sync::Arc<T>
358where
359 T: FromEnv,
360{
361 type Error = T::Error;
362
363 fn inventory() -> Vec<&'static EnvItemInfo> {
364 T::inventory()
365 }
366
367 fn check_inventory() -> Result<(), Vec<&'static EnvItemInfo>> {
368 T::check_inventory()
369 }
370
371 fn from_env() -> Result<Self, FromEnvErr<Self::Error>> {
372 T::from_env().map(std::sync::Arc::new)
373 }
374}
375
376impl<T, U> FromEnv for std::borrow::Cow<'static, U>
377where
378 T: FromEnv,
379 U: std::borrow::ToOwned<Owned = T> + core::fmt::Debug + ?Sized,
380{
381 type Error = T::Error;
382
383 fn inventory() -> Vec<&'static EnvItemInfo> {
384 T::inventory()
385 }
386
387 fn check_inventory() -> Result<(), Vec<&'static EnvItemInfo>> {
388 T::check_inventory()
389 }
390
391 fn from_env() -> Result<Self, FromEnvErr<Self::Error>> {
392 T::from_env().map(std::borrow::Cow::Owned)
393 }
394}
395
396/// Trait for loading primitives from the environment. These are simple types
397/// that should correspond to a single environment variable. It has been
398/// implemented for common integer types, [`String`], [`url::Url`],
399/// [`tracing::Level`], and [`std::time::Duration`].
400///
401/// It aims to make [`FromEnv`] implementations easier to write, by providing a
402/// default implementation for common types.
403///
404/// ## Note on error types
405///
406/// [`FromEnv`] and [`FromEnvVar`] are often deeply nested. This means that
407/// error types are often nested as well. To avoid this, we use a single error
408/// type [`FromEnvVar`] that wraps an inner error type. This allows us to
409/// ensure that env-related errors (e.g. missing env vars) are not lost in the
410/// recursive structure of parsing errors. Environment errors are always at the
411/// top level, and should never be nested. **Do not use [`FromEnvErr<T>`] as
412/// the `Error` associated type in [`FromEnv`].**
413///
414/// ```no_compile
415/// // Do not do this
416/// impl FromEnv for MyType {
417/// type Error = FromEnvErr<MyTypeErr>;
418/// }
419///
420/// // Instead do this:
421/// impl FromEnv for MyType {
422/// type Error = MyTypeErr;
423/// }
424/// ```
425///
426/// ## Implementing [`FromEnv`]
427///
428/// [`FromEnvVar`] is a trait for loading simple types from the environment. It
429/// represents a type that can be loaded from a single environment variable. It
430/// is similar to [`FromStr`] and will usually be using an existing [`FromStr`]
431/// impl.
432///
433/// ```
434/// # use init4_bin_base::utils::from_env::{FromEnvVar, FromEnvErr};
435/// # use std::str::FromStr;
436/// # #[derive(Debug)]
437/// # pub struct MyCoolType;
438/// # impl std::str::FromStr for MyCoolType {
439/// # type Err = std::convert::Infallible;
440/// # fn from_str(s: &str) -> Result<Self, Self::Err> {
441/// # Ok(MyCoolType)
442/// # }
443/// # }
444///
445/// // We can re-use the `FromStr` implementation for our `FromEnvVar` impl.
446/// impl FromEnvVar for MyCoolType {
447/// type Error = <MyCoolType as FromStr>::Err;
448///
449/// fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>>
450/// {
451/// String::from_env_var(env_var).unwrap().parse().map_err(Into::into)
452/// }
453/// }
454/// ```
455pub trait FromEnvVar: core::fmt::Debug + Sized + 'static {
456 /// Error type produced when parsing the primitive.
457 type Error: core::error::Error + Clone + PartialEq + Eq;
458
459 /// Load the primitive from the environment at the given variable.
460 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>>;
461
462 /// Load the primitive from the environment at the given variable. If the
463 /// variable is unset or empty, return the default value.
464 ///
465 /// This function will return an error if the environment variable is set
466 /// but cannot be parsed.
467 fn from_env_var_or(env_var: &str, default: Self) -> Result<Self, FromEnvErr<Self::Error>> {
468 match Self::from_env_var(env_var) {
469 Ok(v) => Ok(v),
470 Err(FromEnvErr::Empty(_)) | Err(FromEnvErr::EnvError(_, _)) => Ok(default),
471 Err(e) => Err(e),
472 }
473 }
474
475 /// Load the primitive from the environment at the given variable. If the
476 /// variable is unset or empty, call the provided function to get the
477 /// default value.
478 ///
479 /// This function will return an error if the environment variable is set
480 /// but cannot be parsed.
481 fn from_env_var_or_else(
482 env_var: &str,
483 default: impl FnOnce() -> Self,
484 ) -> Result<Self, FromEnvErr<Self::Error>> {
485 match Self::from_env_var(env_var) {
486 Ok(v) => Ok(v),
487 Err(FromEnvErr::Empty(_)) | Err(FromEnvErr::EnvError(_, _)) => Ok(default()),
488 Err(e) => Err(e),
489 }
490 }
491
492 /// Load the primitive from the environment at the given variable. If the
493 /// variable is unset or empty, return the value generated by
494 /// [`Default::default`].
495 ///
496 /// This function will return an error if the environment variable is set
497 /// but cannot be parsed.
498 fn from_env_var_or_default(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>>
499 where
500 Self: Default,
501 {
502 Self::from_env_var_or_else(env_var, Self::default)
503 }
504}
505
506impl<T> FromEnvVar for Option<T>
507where
508 T: FromEnvVar,
509{
510 type Error = T::Error;
511
512 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
513 match std::env::var(env_var) {
514 Ok(s) if s.is_empty() => Ok(None),
515 Ok(_) => T::from_env_var(env_var).map(Some),
516 Err(_) => Ok(None),
517 }
518 }
519}
520
521impl<T> FromEnvVar for Box<T>
522where
523 T: FromEnvVar,
524{
525 type Error = T::Error;
526
527 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
528 T::from_env_var(env_var).map(Box::new)
529 }
530}
531
532impl<T> FromEnvVar for std::sync::Arc<T>
533where
534 T: FromEnvVar,
535{
536 type Error = T::Error;
537
538 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
539 T::from_env_var(env_var).map(std::sync::Arc::new)
540 }
541}
542
543impl<T, U> FromEnvVar for std::borrow::Cow<'static, U>
544where
545 T: FromEnvVar,
546 U: std::borrow::ToOwned<Owned = T> + core::fmt::Debug + ?Sized,
547{
548 type Error = T::Error;
549
550 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
551 T::from_env_var(env_var).map(std::borrow::Cow::Owned)
552 }
553}
554
555impl FromEnvVar for String {
556 type Error = std::convert::Infallible;
557
558 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
559 std::env::var(env_var).map_err(|_| FromEnvErr::empty(env_var))
560 }
561}
562
563impl FromEnvVar for std::time::Duration {
564 type Error = ParseIntError;
565
566 fn from_env_var(s: &str) -> Result<Self, FromEnvErr<Self::Error>> {
567 u64::from_env_var(s).map(Self::from_millis)
568 }
569}
570
571impl<T> FromEnvVar for Vec<T>
572where
573 T: From<String> + core::fmt::Debug + 'static,
574{
575 type Error = Infallible;
576
577 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
578 let s = std::env::var(env_var).map_err(|e| FromEnvErr::env_err(env_var, e))?;
579 if s.is_empty() {
580 return Ok(vec![]);
581 }
582 Ok(s.split(',')
583 .map(str::to_string)
584 .map(Into::into)
585 .collect::<Vec<_>>())
586 }
587}
588
589macro_rules! impl_for_parseable {
590 ($($t:ty),*) => {
591 $(
592 impl FromEnvVar for $t {
593 type Error = <$t as FromStr>::Err;
594
595 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
596 parse_env_if_present(env_var)
597 }
598 }
599 )*
600 }
601}
602
603impl_for_parseable!(
604 u8,
605 u16,
606 u32,
607 u64,
608 u128,
609 usize,
610 i8,
611 i16,
612 i32,
613 i64,
614 i128,
615 isize,
616 url::Url,
617 SignetConstants,
618 SignetEnvironmentConstants,
619 SignetSystemConstants,
620 HostConstants,
621 RollupConstants
622);
623
624#[cfg(feature = "alloy")]
625impl_for_parseable!(
626 alloy::primitives::Address,
627 alloy::primitives::Bytes,
628 alloy::primitives::U256
629);
630
631#[cfg(feature = "alloy")]
632impl<const N: usize> FromEnvVar for alloy::primitives::FixedBytes<N> {
633 type Error = <alloy::primitives::FixedBytes<N> as FromStr>::Err;
634
635 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
636 parse_env_if_present(env_var)
637 }
638}
639
640impl FromEnvVar for bool {
641 type Error = std::str::ParseBoolError;
642
643 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
644 let s: String = std::env::var(env_var).map_err(|e| FromEnvErr::env_err(env_var, e))?;
645 Ok(!s.is_empty())
646 }
647}
648
649/// Error type for parsing tracing levels from the environment.
650#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
651#[error("failed to parse tracing level from environment variable")]
652pub struct LevelParseError;
653
654impl From<ParseLevelError> for LevelParseError {
655 fn from(_: ParseLevelError) -> Self {
656 LevelParseError
657 }
658}
659
660impl FromEnvVar for tracing::Level {
661 type Error = LevelParseError;
662
663 fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
664 let s: String = std::env::var(env_var).map_err(|e| FromEnvErr::env_err(env_var, e))?;
665 s.parse()
666 .map_err(Into::into)
667 .map_err(FromEnvErr::parse_error)
668 }
669}
670
671#[cfg(test)]
672mod test {
673 use std::{borrow::Cow, time::Duration};
674
675 use super::*;
676
677 fn set<T>(env: &str, val: &T)
678 where
679 T: ToString + ?Sized,
680 {
681 std::env::set_var(env, val.to_string());
682 }
683
684 fn load_expect_err<T>(env: &str, err: FromEnvErr<T::Error>)
685 where
686 T: FromEnvVar,
687 T::Error: PartialEq,
688 {
689 let res = T::from_env_var(env).unwrap_err();
690 assert_eq!(res, err);
691 }
692
693 fn test<T>(env: &str, val: T)
694 where
695 T: ToString + FromEnvVar + PartialEq + std::fmt::Debug,
696 {
697 set(env, &val);
698
699 let res = T::from_env_var(env).unwrap();
700 assert_eq!(res, val);
701 }
702
703 fn test_expect_err<T, U>(env: &str, value: U, err: FromEnvErr<T::Error>)
704 where
705 T: FromEnvVar,
706 U: ToString,
707 T::Error: PartialEq,
708 {
709 set(env, &value);
710 load_expect_err::<T>(env, err);
711 }
712
713 #[test]
714 fn test_primitives() {
715 test("U8", 42u8);
716 test("U16", 42u16);
717 test("U32", 42u32);
718 test("U64", 42u64);
719 test("U128", 42u128);
720 test("Usize", 42usize);
721 test("I8", 42i8);
722 test("I8-NEG", -42i16);
723 test("I16", 42i16);
724 test("I32", 42i32);
725 test("I64", 42i64);
726 test("I128", 42i128);
727 test("Isize", 42isize);
728 test("String", "hello".to_string());
729 test("Url", url::Url::parse("http://example.com").unwrap());
730 test("Level", tracing::Level::INFO);
731 }
732
733 #[test]
734 fn test_duration() {
735 let amnt = 42;
736 let val = Duration::from_millis(42);
737
738 set("Duration", &amnt);
739 let res = Duration::from_env_var("Duration").unwrap();
740
741 assert_eq!(res, val);
742 }
743
744 #[test]
745 fn test_a_few_errors() {
746 test_expect_err::<u8, _>(
747 "U8_",
748 30000u16,
749 FromEnvErr::parse_error("30000".parse::<u8>().unwrap_err()),
750 );
751
752 test_expect_err::<u8, _>("U8_", "", FromEnvErr::empty("U8_"));
753 }
754
755 #[test]
756 fn is_cow_str_from_env_var() {
757 let s = "hello";
758 set("COW", s);
759 let res: Cow<'static, str> = Cow::from_env_var("COW").unwrap();
760 assert_eq!(res, Cow::<'static, str>::Owned(s.to_owned()));
761 }
762}