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}