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