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