env_flags/
lib.rs

1/*!
2This crate exports the `env_flags` macro, which allows a convenient way to declare static
3environment variables with optional default values and custom parsing functions.
4
5Currently, this crate requires a Rust compiler of at least `1.80` as it uses `std::sync::LazyLock` under the hood.
6
7# Examples
8```rust
9use env_flags::env_flags;
10
11use std::time::Duration;
12
13env_flags! {
14    /// Required env var, panics if missing.
15    AUTH_TOKEN: &str;
16    /// Env var with a default value if not specified.
17    pub(crate) PORT: u16 = 8080;
18    /// An optional env var.
19    pub OVERRIDE_HOSTNAME: Option<&str> = None;
20
21    /// `Duration` by default is parsed as `f64` seconds.
22    TIMEOUT: Duration = Duration::from_secs(5);
23    /// Custom parsing function, takes a `String` and returns a `Result<Duration>`.
24    TIMEOUT_MS: Duration = Duration::from_millis(30), |value| {
25        value.parse().map(Duration::from_millis)
26    };
27
28    /// `bool` can be true, false, 1, or 0 (case insensitive)
29    /// eg. export ENABLE_FEATURE="true"
30    pub ENABLE_FEATURE: bool = true;
31
32    /// `Vec<T>` by default is parsed as a comma-seprated string
33    /// eg. export VALID_PORTS="80,443,9121"
34    pub VALID_PORTS: Vec<u16> = vec![80, 443, 9121];
35
36
37    // Attributes are also captured, including docs
38
39    #[cfg(target_os = "linux")]
40    /// operating system
41    pub OS: &str = "linux";
42    #[cfg(not(target_os = "linux"))]
43    /// operating system
44    pub OS: &str = "not linux";
45}
46```
47
48For custom types, you can either specify a parsing function manually (see above `TIMEOUT_MS` example), or you can implement the `ParseEnv` trait. An implementation for `ParseEnv` is included for most std types.
49
50*/
51use std::convert::Infallible;
52use std::fmt;
53use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
54use std::ops::Deref;
55use std::path::PathBuf;
56use std::sync::LazyLock;
57use std::time::Duration;
58
59/// Define the parsing function for a type from a `String` environment variable.
60///
61/// Check the source for the builtin type definitions for this trait if you're concerned about the
62/// parsing logic.
63pub trait ParseEnv: Sized {
64    /// The `std::fmt::Display` of this error message will appear in the panic message when parsing
65    /// fails.
66    type Err: fmt::Display;
67    /// Tries to parse the value from `std::env::var`.
68    fn parse_env(value: String) -> Result<Self, Self::Err>;
69}
70
71/// Intermediate error type used in parsing failures to generate helpful messages.
72#[derive(Debug, Clone)]
73pub struct ParseError {
74    type_name: &'static str,
75    msg: String,
76}
77
78impl ParseError {
79    pub fn from_msg<T, S: ToString>(msg: S) -> Self {
80        Self {
81            type_name: std::any::type_name::<T>(),
82            msg: msg.to_string(),
83        }
84    }
85
86    fn with_type_name<T>(mut self) -> Self {
87        self.type_name = std::any::type_name::<T>();
88        self
89    }
90}
91
92impl fmt::Display for ParseError {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        write!(f, "failed to parse as {}: {}", self.type_name, &self.msg)
95    }
96}
97
98/// Implements `ParseEnv` for common types based on `std::str::FromStr`.
99macro_rules! gen_parse_env_using_fromstr {
100    ($ty: ty) => {
101        impl ParseEnv for $ty {
102            type Err = $crate::ParseError;
103
104            fn parse_env(value: String) -> Result<Self, Self::Err> {
105                value
106                    .parse()
107                    .map_err(|e| $crate::ParseError::from_msg::<Self, _>(e))
108            }
109        }
110    };
111}
112
113gen_parse_env_using_fromstr!(f32);
114gen_parse_env_using_fromstr!(f64);
115gen_parse_env_using_fromstr!(i8);
116gen_parse_env_using_fromstr!(i16);
117gen_parse_env_using_fromstr!(i32);
118gen_parse_env_using_fromstr!(i64);
119gen_parse_env_using_fromstr!(i128);
120gen_parse_env_using_fromstr!(isize);
121gen_parse_env_using_fromstr!(u8);
122gen_parse_env_using_fromstr!(u16);
123gen_parse_env_using_fromstr!(u32);
124gen_parse_env_using_fromstr!(u64);
125gen_parse_env_using_fromstr!(u128);
126gen_parse_env_using_fromstr!(usize);
127gen_parse_env_using_fromstr!(IpAddr);
128gen_parse_env_using_fromstr!(Ipv4Addr);
129gen_parse_env_using_fromstr!(Ipv6Addr);
130gen_parse_env_using_fromstr!(PathBuf);
131gen_parse_env_using_fromstr!(SocketAddr);
132
133impl ParseEnv for String {
134    type Err = Infallible;
135
136    fn parse_env(value: String) -> Result<Self, Self::Err> {
137        Ok(value)
138    }
139}
140
141impl ParseEnv for &'static str {
142    type Err = Infallible;
143
144    fn parse_env(value: String) -> Result<Self, Self::Err> {
145        Ok(Box::leak(Box::new(value)))
146    }
147}
148
149/// `Duration` by default is parsed as `f64` seconds;
150impl ParseEnv for Duration {
151    type Err = ParseError;
152
153    fn parse_env(value: String) -> Result<Self, Self::Err> {
154        match ParseEnv::parse_env(value) {
155            Ok(secs) => Ok(Duration::from_secs_f64(secs)),
156            Err(e) => Err(e.with_type_name::<Self>()),
157        }
158    }
159}
160
161/// `Vec<T>` is by default parsed as comma-separated values.
162impl<T> ParseEnv for Vec<T>
163where
164    T: ParseEnv,
165{
166    type Err = <T as ParseEnv>::Err;
167
168    fn parse_env(value: String) -> Result<Self, Self::Err> {
169        value
170            .split(',')
171            .map(|v| ParseEnv::parse_env(v.to_owned()))
172            .collect()
173    }
174}
175
176impl<T> ParseEnv for Option<T>
177where
178    T: ParseEnv,
179{
180    type Err = <T as ParseEnv>::Err;
181
182    fn parse_env(value: String) -> Result<Self, Self::Err> {
183        Ok(Some(ParseEnv::parse_env(value)?))
184    }
185}
186
187/// `bool` allows two common conventions:
188/// - String either "true" or "false" (case insensitive)
189/// - Integer either 0 or 1
190///
191/// Anything else will result in a `ParseError`.
192impl ParseEnv for bool {
193    type Err = ParseError;
194
195    fn parse_env(mut value: String) -> Result<Self, Self::Err> {
196        value.make_ascii_lowercase();
197        match value.as_str() {
198            "true" | "1" => Ok(true),
199            "false" | "0" => Ok(false),
200            _ => Err(ParseError::from_msg::<Self, _>(
201                "expected either true or false",
202            )),
203        }
204    }
205}
206
207/// Static lazily evaluated environment variable.
208pub struct LazyEnv<T> {
209    inner: LazyLock<T>,
210}
211
212impl<T> LazyEnv<T> {
213    #[inline]
214    #[doc(hidden)]
215    pub const fn new(init_fn: fn() -> T) -> Self {
216        Self {
217            inner: LazyLock::new(init_fn),
218        }
219    }
220}
221
222impl<T> Deref for LazyEnv<T> {
223    type Target = T;
224
225    fn deref(&self) -> &Self::Target {
226        &self.inner
227    }
228}
229
230impl<T> fmt::Debug for LazyEnv<T>
231where
232    T: fmt::Debug,
233{
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        let inner = &*self.inner;
236        inner.fmt(f)
237    }
238}
239
240impl<T> fmt::Display for LazyEnv<T>
241where
242    T: fmt::Display,
243{
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        let inner = &*self.inner;
246        inner.fmt(f)
247    }
248}
249
250/// Helper function for better compilation errors
251#[doc(hidden)]
252#[inline]
253pub fn __apply_parse_fn<F, T, E>(func: F, key: &'static str, val: String) -> T
254where
255    F: Fn(String) -> Result<T, E>,
256    E: fmt::Display,
257{
258    match func(val) {
259        Ok(val) => val,
260        Err(e) => __invalid_env_var(key, e),
261    }
262}
263
264#[doc(hidden)]
265pub fn __invalid_env_var(key: &'static str, err: impl fmt::Display) -> ! {
266    panic!("Invalid environment variable {}, {}", key, err)
267}
268
269#[doc(hidden)]
270pub fn __missing_env_var(key: &'static str) -> ! {
271    panic!("Missing required environment variable {}", key)
272}
273
274/// private macro for recursively expanding `env_flag`
275#[doc(hidden)]
276#[macro_export(local_inner_macros)]
277macro_rules! __env_flag_inner {
278    ($(#[$attr:meta])* ($($vis:tt)*) $key:ident : $ty:ty = $default:expr, $parse_fn:expr; $($rem:tt)*) => {
279        $(#[$attr])*
280        $($vis)* static $key: $crate::LazyEnv<$ty> = $crate::LazyEnv::new(|| {
281            match ::std::env::var(::std::stringify!($key)) {
282                Ok(value) => $crate::__apply_parse_fn($parse_fn, ::std::stringify!($key), value),
283                Err(_) => $default(),
284            }
285        });
286
287        env_flags!($($rem)*);
288    };
289}
290
291/// Declare environment variables with optional defaults and parsing functions.
292///
293/// Values are static and lazily evaluated once the first time they are dereferenced.
294///
295/// See the module-level documents for examples.
296#[macro_export(local_inner_macros)]
297macro_rules! env_flags {
298    // key: type;
299    ($(#[$attr:meta])* $key:ident : $ty:ty; $($rem:tt)*) => {
300        __env_flag_inner!($(#[$attr])* () $key : $ty = || $crate::__missing_env_var(::std::stringify!($key)), $crate::ParseEnv::parse_env; $($rem)*);
301    };
302    ($(#[$attr:meta])* pub $key:ident : $ty:ty; $($rem:tt)*) => {
303        __env_flag_inner!($(#[$attr])* (pub) $key : $ty = || $crate::__missing_env_var(::std::stringify!($key)), $crate::ParseEnv::parse_env; $($rem)*);
304    };
305    ($(#[$attr:meta])* pub ($($vis:tt)+) $key:ident : $ty:ty; $($rem:tt)*) => {
306        __env_flag_inner!($(#[$attr])* (pub ($($vis)+)) $key : $ty = || $crate::__missing_env_var(::std::stringify!($key)), $crate::ParseEnv::parse_env; $($rem)*);
307    };
308
309    // key: type = default;
310    ($(#[$attr:meta])* $key:ident : $ty:ty = $default:expr; $($rem:tt)*) => {
311        __env_flag_inner!($(#[$attr])* () $key : $ty = || $default, $crate::ParseEnv::parse_env; $($rem)*);
312    };
313    ($(#[$attr:meta])* pub $key:ident : $ty:ty = $default:expr; $($rem:tt)*) => {
314        __env_flag_inner!($(#[$attr])* (pub) $key : $ty = || $default, $crate::ParseEnv::parse_env; $($rem)*);
315    };
316    ($(#[$attr:meta])* pub ($($vis:tt)+) $key:ident : $ty:ty = $default:expr; $($rem:tt)*) => {
317        __env_flag_inner!($(#[$attr])* (pub ($($vis)+)) $key : $ty = || $default, $crate::ParseEnv::parse_env; $($rem)*);
318    };
319
320    // key: type, parse_fn;
321    ($(#[$attr:meta])* $key:ident : $ty:ty, $parse_fn:expr; $($rem:tt)*) => {
322        __env_flag_inner!($(#[$attr])* () $key : $ty = || $crate::__missing_env_var(::std::stringify!($key)), $parse_fn; $($rem)*);
323    };
324    ($(#[$attr:meta])* pub $key:ident : $ty:ty, $parse_fn:expr; $($rem:tt)*) => {
325        __env_flag_inner!($(#[$attr])* (pub) $key : $ty = || $crate::__missing_env_var(::std::stringify!($key)), $parse_fn; $($rem)*);
326    };
327    ($(#[$attr:meta])* pub ($($vis:tt)+) $key:ident : $ty:ty, $parse_fn:expr; $($rem:tt)*) => {
328        __env_flag_inner!($(#[$attr])* (pub ($($vis)+)) $key : $ty = || $crate::__missing_env_var(::std::stringify!($key)), $parse_fn; $($rem)*);
329    };
330
331    // key: type = default, parse_fn;
332    ($(#[$attr:meta])* $key:ident : $ty:ty = $default:expr, $parse_fn:expr; $($rem:tt)*) => {
333        __env_flag_inner!($(#[$attr])* () $key : $ty = || $default, $parse_fn; $($rem)*);
334    };
335    ($(#[$attr:meta])* pub $key:ident : $ty:ty = $default:expr, $parse_fn:expr; $($rem:tt)*) => {
336        __env_flag_inner!($(#[$attr])* (pub) $key : $ty = || $default, $parse_fn; $($rem)*);
337    };
338    ($(#[$attr:meta])* pub ($($vis:tt)+) $key:ident : $ty:ty = $default:expr, $parse_fn:expr; $($rem:tt)*) => {
339        __env_flag_inner!($(#[$attr])* (pub ($($vis)+)) $key : $ty = || $default, $parse_fn; $($rem)*);
340    };
341
342    () => {};
343}
344
345#[cfg(test)]
346mod test {
347    use super::*;
348
349    #[test]
350    fn test_key_type_set() {
351        std::env::set_var("ENV_FLAGS_TEST_KEY_TYPE_SET_PRIV", "80");
352        std::env::set_var("ENV_FLAGS_TEST_KEY_TYPE_SET_CRATE", "81");
353        std::env::set_var("ENV_FLAGS_TEST_KEY_TYPE_SET_PUB", "82");
354        env_flags! {
355            ENV_FLAGS_TEST_KEY_TYPE_SET_PRIV: u16;
356            pub(crate) ENV_FLAGS_TEST_KEY_TYPE_SET_CRATE: u16;
357            pub ENV_FLAGS_TEST_KEY_TYPE_SET_PUB: u16;
358        };
359        assert_eq!(*ENV_FLAGS_TEST_KEY_TYPE_SET_PRIV, 80u16);
360        assert_eq!(*ENV_FLAGS_TEST_KEY_TYPE_SET_CRATE, 81u16);
361        assert_eq!(*ENV_FLAGS_TEST_KEY_TYPE_SET_PUB, 82u16);
362    }
363
364    #[test]
365    #[should_panic]
366    fn test_key_type_unset() {
367        env_flags! {
368            pub ENV_FLAGS_TEST_KEY_TYPE_UNSET: u16;
369        };
370        let _ = *ENV_FLAGS_TEST_KEY_TYPE_UNSET;
371    }
372
373    #[test]
374    fn test_default() {
375        std::env::set_var("ENV_FLAGS_TEST_DEFAULT_SET_PRIV", "goodbye");
376        std::env::set_var("ENV_FLAGS_TEST_DEFAULT_SET_CRATE", "goodbye");
377        std::env::set_var("ENV_FLAGS_TEST_DEFAULT_SET_PUB", "goodbye");
378        env_flags! {
379            ENV_FLAGS_TEST_DEFAULT_SET_PRIV: &str = "hello";
380            pub(crate) ENV_FLAGS_TEST_DEFAULT_SET_CRATE: &str = "hello";
381            pub ENV_FLAGS_TEST_DEFAULT_SET_PUB: &str = "hello";
382
383            pub ENV_FLAGS_TEST_DEFAULT_UNSET: &str = "world";
384        };
385        assert_eq!(*ENV_FLAGS_TEST_DEFAULT_SET_PRIV, "goodbye");
386        assert_eq!(*ENV_FLAGS_TEST_DEFAULT_SET_CRATE, "goodbye");
387        assert_eq!(*ENV_FLAGS_TEST_DEFAULT_SET_PUB, "goodbye");
388
389        assert_eq!(*ENV_FLAGS_TEST_DEFAULT_UNSET, "world");
390    }
391
392    #[test]
393    fn test_parse_fn() {
394        std::env::set_var("ENV_FLAGS_TEST_PARSE_FN_PRIV", "250");
395        std::env::set_var("ENV_FLAGS_TEST_PARSE_FN_CRATE", "5");
396        std::env::set_var("ENV_FLAGS_TEST_PARSE_FN_PUB", "120");
397        env_flags! {
398            ENV_FLAGS_TEST_PARSE_FN_PRIV: Duration, |val| val.parse().map(Duration::from_millis);
399            pub(crate) ENV_FLAGS_TEST_PARSE_FN_CRATE: Duration, |val| val.parse().map(Duration::from_secs);
400            pub ENV_FLAGS_TEST_PARSE_FN_PUB: Duration, |val| val.parse().map(Duration::from_nanos);
401        };
402        assert_eq!(*ENV_FLAGS_TEST_PARSE_FN_PRIV, Duration::from_millis(250));
403        assert_eq!(*ENV_FLAGS_TEST_PARSE_FN_CRATE, Duration::from_secs(5));
404        assert_eq!(*ENV_FLAGS_TEST_PARSE_FN_PUB, Duration::from_nanos(120));
405    }
406
407    #[test]
408    #[should_panic]
409    fn test_parse_fn_unset() {
410        env_flags! {
411            pub(crate) ENV_FLAGS_TEST_PARSE_FN_UNSET: Duration, |val| val.parse().map(Duration::from_millis);
412        };
413        let _ = *ENV_FLAGS_TEST_PARSE_FN_UNSET;
414    }
415
416    #[test]
417    fn test_parse_fn_default() {
418        std::env::set_var("ENV_FLAGS_TEST_PARSE_FN_DEFAULT_PRIV", "10");
419        std::env::set_var("ENV_FLAGS_TEST_PARSE_FN_DEFAULT_CRATE", "11");
420        std::env::set_var("ENV_FLAGS_TEST_PARSE_FN_DEFAULT_PUB", "12");
421        env_flags! {
422            ENV_FLAGS_TEST_PARSE_FN_DEFAULT_PRIV: Duration = Duration::from_millis(5), |val| val.parse().map(Duration::from_millis);
423            pub(crate) ENV_FLAGS_TEST_PARSE_FN_DEFAULT_CRATE: Duration = Duration::from_millis(5), |val| val.parse().map(Duration::from_millis);
424            pub ENV_FLAGS_TEST_PARSE_FN_DEFAULT_PUB: Duration = Duration::from_millis(5), |val| val.parse().map(Duration::from_millis);
425
426            ENV_FLAGS_TEST_PARSE_FN_DEFAULT_UNSET: Duration = Duration::from_millis(5), |val| val.parse().map(Duration::from_millis);
427        };
428        assert_eq!(
429            *ENV_FLAGS_TEST_PARSE_FN_DEFAULT_PRIV,
430            Duration::from_millis(10)
431        );
432        assert_eq!(
433            *ENV_FLAGS_TEST_PARSE_FN_DEFAULT_CRATE,
434            Duration::from_millis(11)
435        );
436        assert_eq!(
437            *ENV_FLAGS_TEST_PARSE_FN_DEFAULT_PUB,
438            Duration::from_millis(12)
439        );
440        assert_eq!(
441            *ENV_FLAGS_TEST_PARSE_FN_DEFAULT_UNSET,
442            Duration::from_millis(5)
443        );
444    }
445
446    #[test]
447    fn test_types_f32() {
448        std::env::set_var("ENV_FLAGS_TEST_TYPES_F32_POS", "1.2");
449        std::env::set_var("ENV_FLAGS_TEST_TYPES_F32_NEG", "-3.2");
450        std::env::set_var("ENV_FLAGS_TEST_TYPES_F32_NAN", "nan");
451        std::env::set_var("ENV_FLAGS_TEST_TYPES_F32_INF", "inf");
452        env_flags! {
453            ENV_FLAGS_TEST_TYPES_F32_POS: f32;
454            ENV_FLAGS_TEST_TYPES_F32_NEG: f32;
455            ENV_FLAGS_TEST_TYPES_F32_NAN: f32;
456            ENV_FLAGS_TEST_TYPES_F32_INF: f32;
457        };
458        assert_eq!(*ENV_FLAGS_TEST_TYPES_F32_POS, 1.2);
459        assert_eq!(*ENV_FLAGS_TEST_TYPES_F32_NEG, -3.2);
460        assert!(ENV_FLAGS_TEST_TYPES_F32_NAN.is_nan());
461        assert!(ENV_FLAGS_TEST_TYPES_F32_INF.is_infinite());
462    }
463
464    #[test]
465    #[should_panic]
466    fn test_invalid_f32() {
467        std::env::set_var("ENV_FLAGS_TEST_INVALID_F32", "cat");
468        env_flags! {
469            ENV_FLAGS_TEST_INVALID_F32: f32 = 0.0;
470        };
471        let _ = *ENV_FLAGS_TEST_INVALID_F32;
472    }
473
474    #[test]
475    fn test_types_f64() {
476        std::env::set_var("ENV_FLAGS_TEST_TYPES_F64_POS", "41.1");
477        std::env::set_var("ENV_FLAGS_TEST_TYPES_F64_NEG", "-0.4");
478        std::env::set_var("ENV_FLAGS_TEST_TYPES_F64_NAN", "nan");
479        std::env::set_var("ENV_FLAGS_TEST_TYPES_F64_INF", "inf");
480        env_flags! {
481            ENV_FLAGS_TEST_TYPES_F64_POS: f64;
482            ENV_FLAGS_TEST_TYPES_F64_NEG: f64;
483            ENV_FLAGS_TEST_TYPES_F64_NAN: f64;
484            ENV_FLAGS_TEST_TYPES_F64_INF: f64;
485        };
486        assert_eq!(*ENV_FLAGS_TEST_TYPES_F64_POS, 41.1);
487        assert_eq!(*ENV_FLAGS_TEST_TYPES_F64_NEG, -0.4);
488        assert!(ENV_FLAGS_TEST_TYPES_F64_NAN.is_nan());
489        assert!(ENV_FLAGS_TEST_TYPES_F64_INF.is_infinite());
490    }
491
492    #[test]
493    #[should_panic]
494    fn test_invalid_f64() {
495        std::env::set_var("ENV_FLAGS_TEST_INVALID_F64", "cat");
496        env_flags! {
497            ENV_FLAGS_TEST_INVALID_F64: f64 = 0.0;
498        };
499        let _ = *ENV_FLAGS_TEST_INVALID_F64;
500    }
501
502    #[test]
503    fn test_types_i8() {
504        std::env::set_var("ENV_FLAGS_TEST_TYPES_I8_POS", "4");
505        std::env::set_var("ENV_FLAGS_TEST_TYPES_I8_NEG", "-4");
506        env_flags! {
507            ENV_FLAGS_TEST_TYPES_I8_POS: i8;
508            ENV_FLAGS_TEST_TYPES_I8_NEG: i8;
509        };
510        assert_eq!(*ENV_FLAGS_TEST_TYPES_I8_POS, 4);
511        assert_eq!(*ENV_FLAGS_TEST_TYPES_I8_NEG, -4);
512    }
513
514    #[test]
515    #[should_panic]
516    fn test_invalid_i8() {
517        std::env::set_var("ENV_FLAGS_TEST_INVALID_I8", "128");
518        env_flags! {
519            ENV_FLAGS_TEST_INVALID_I8: i8;
520        };
521        let _ = *ENV_FLAGS_TEST_INVALID_I8;
522    }
523
524    #[test]
525    fn test_types_i16() {
526        std::env::set_var("ENV_FLAGS_TEST_TYPES_I16_POS", "2559");
527        std::env::set_var("ENV_FLAGS_TEST_TYPES_I16_NEG", "-2559");
528        env_flags! {
529            ENV_FLAGS_TEST_TYPES_I16_POS: i16;
530            ENV_FLAGS_TEST_TYPES_I16_NEG: i16;
531        };
532        assert_eq!(*ENV_FLAGS_TEST_TYPES_I16_POS, 2559);
533        assert_eq!(*ENV_FLAGS_TEST_TYPES_I16_NEG, -2559);
534    }
535
536    #[test]
537    #[should_panic]
538    fn test_invalid_i16() {
539        std::env::set_var("ENV_FLAGS_TEST_INVALID_I16", "32768");
540        env_flags! {
541            ENV_FLAGS_TEST_INVALID_I16: i16 = 0;
542        };
543        let _ = *ENV_FLAGS_TEST_INVALID_I16;
544    }
545
546    #[test]
547    fn test_types_i32() {
548        std::env::set_var("ENV_FLAGS_TEST_TYPES_I32_POS", "124");
549        std::env::set_var("ENV_FLAGS_TEST_TYPES_I32_NEG", "-124");
550        env_flags! {
551            ENV_FLAGS_TEST_TYPES_I32_POS: i32;
552            ENV_FLAGS_TEST_TYPES_I32_NEG: i32;
553        };
554        assert_eq!(*ENV_FLAGS_TEST_TYPES_I32_POS, 124);
555        assert_eq!(*ENV_FLAGS_TEST_TYPES_I32_NEG, -124);
556    }
557
558    #[test]
559    #[should_panic]
560    fn test_invalid_i32() {
561        std::env::set_var("ENV_FLAGS_TEST_INVALID_I32", "2147483648");
562        env_flags! {
563            ENV_FLAGS_TEST_INVALID_I32: i32 = 0;
564        };
565        let _ = *ENV_FLAGS_TEST_INVALID_I32;
566    }
567
568    #[test]
569    fn test_types_i64() {
570        std::env::set_var("ENV_FLAGS_TEST_TYPES_I64_POS", "13966932211");
571        std::env::set_var("ENV_FLAGS_TEST_TYPES_I64_NEG", "-13966932211");
572        env_flags! {
573            ENV_FLAGS_TEST_TYPES_I64_POS: i64;
574            ENV_FLAGS_TEST_TYPES_I64_NEG: i64;
575        }
576        assert_eq!(*ENV_FLAGS_TEST_TYPES_I64_POS, 13966932211);
577        assert_eq!(*ENV_FLAGS_TEST_TYPES_I64_NEG, -13966932211);
578    }
579
580    #[test]
581    fn test_types_i128() {
582        std::env::set_var("ENV_FLAGS_TEST_TYPES_I128_POS", "1020304995959399");
583        std::env::set_var("ENV_FLAGS_TEST_TYPES_I128_NEG", "-1020304995959399");
584        env_flags! {
585            ENV_FLAGS_TEST_TYPES_I128_POS: i128;
586            ENV_FLAGS_TEST_TYPES_I128_NEG: i128;
587        };
588        assert_eq!(*ENV_FLAGS_TEST_TYPES_I128_POS, 1020304995959399);
589        assert_eq!(*ENV_FLAGS_TEST_TYPES_I128_NEG, -1020304995959399);
590    }
591
592    #[test]
593    fn test_types_isize() {
594        std::env::set_var("ENV_FLAGS_TEST_TYPES_ISIZE_POS", "29294");
595        std::env::set_var("ENV_FLAGS_TEST_TYPES_ISIZE_NEG", "-29294");
596        env_flags! {
597            ENV_FLAGS_TEST_TYPES_ISIZE_POS: isize;
598            ENV_FLAGS_TEST_TYPES_ISIZE_NEG: isize;
599        };
600        assert_eq!(*ENV_FLAGS_TEST_TYPES_ISIZE_POS, 29294);
601        assert_eq!(*ENV_FLAGS_TEST_TYPES_ISIZE_NEG, -29294);
602    }
603
604    #[test]
605    fn test_types_u8() {
606        std::env::set_var("ENV_FLAGS_TEST_TYPES_U8", "10");
607        env_flags! {
608            ENV_FLAGS_TEST_TYPES_U8: u8;
609        };
610        assert_eq!(*ENV_FLAGS_TEST_TYPES_U8, 10);
611    }
612
613    #[test]
614    fn test_types_u16() {
615        std::env::set_var("ENV_FLAGS_TEST_TYPES_U16", "7432");
616        env_flags! {
617            ENV_FLAGS_TEST_TYPES_U16: u16;
618        };
619        assert_eq!(*ENV_FLAGS_TEST_TYPES_U16, 7432);
620    }
621
622    #[test]
623    fn test_types_u32() {
624        std::env::set_var("ENV_FLAGS_TEST_TYPES_U32", "305528");
625        env_flags! {
626            ENV_FLAGS_TEST_TYPES_U32: u32;
627        };
628        assert_eq!(*ENV_FLAGS_TEST_TYPES_U32, 305528);
629    }
630
631    #[test]
632    fn test_types_u64() {
633        std::env::set_var("ENV_FLAGS_TEST_TYPES_U64", "123456789");
634        env_flags! {
635            ENV_FLAGS_TEST_TYPES_U64: u64;
636        };
637        assert_eq!(*ENV_FLAGS_TEST_TYPES_U64, 123456789);
638    }
639
640    #[test]
641    fn test_types_u128() {
642        std::env::set_var("ENV_FLAGS_TEST_TYPES_U128", "2919239");
643        env_flags! {
644            ENV_FLAGS_TEST_TYPES_U128: u128;
645        };
646        assert_eq!(*ENV_FLAGS_TEST_TYPES_U128, 2919239);
647    }
648
649    #[test]
650    fn test_types_usize() {
651        std::env::set_var("ENV_FLAGS_TEST_TYPES_USIZE", "2939");
652        env_flags! {
653            ENV_FLAGS_TEST_TYPES_USIZE: usize;
654        };
655        assert_eq!(*ENV_FLAGS_TEST_TYPES_USIZE, 2939);
656    }
657
658    #[test]
659    fn test_types_ipaddr() {
660        std::env::set_var("ENV_FLAGS_TEST_TYPES_IPADDR", "0.0.0.0");
661        env_flags! {
662            ENV_FLAGS_TEST_TYPES_IPADDR: IpAddr;
663        };
664        assert_eq!(*ENV_FLAGS_TEST_TYPES_IPADDR, {
665            let ip: Ipv4Addr = "0.0.0.0".parse().unwrap();
666            ip
667        });
668    }
669
670    #[test]
671    fn test_types_ipv4addr() {
672        std::env::set_var("ENV_FLAGS_TEST_TYPES_IPV4ADDR", "127.0.0.1");
673        env_flags! {
674            ENV_FLAGS_TEST_TYPES_IPV4ADDR: Ipv4Addr;
675        };
676        assert_eq!(*ENV_FLAGS_TEST_TYPES_IPV4ADDR, {
677            let ip: Ipv4Addr = "127.0.0.1".parse().unwrap();
678            ip
679        });
680    }
681
682    #[test]
683    fn test_types_ipv6addr() {
684        std::env::set_var(
685            "ENV_FLAGS_TEST_TYPES_IPV6ADDR",
686            "2001:0000:130F:0000:0000:09C0:876A:130B",
687        );
688        env_flags! {
689            ENV_FLAGS_TEST_TYPES_IPV6ADDR: Ipv6Addr;
690        };
691        assert_eq!(*ENV_FLAGS_TEST_TYPES_IPV6ADDR, {
692            let ip: Ipv6Addr = "2001:0000:130F:0000:0000:09C0:876A:130B".parse().unwrap();
693            ip
694        });
695    }
696
697    #[test]
698    fn test_types_socketaddr() {
699        std::env::set_var("ENV_FLAGS_TEST_TYPES_SOCKETADDR", "192.168.0.1:8080");
700        env_flags! {
701            ENV_FLAGS_TEST_TYPES_SOCKETADDR: SocketAddr;
702        };
703        assert_eq!(
704            *ENV_FLAGS_TEST_TYPES_SOCKETADDR,
705            "192.168.0.1:8080".parse().unwrap()
706        );
707    }
708
709    #[test]
710    fn test_types_pathbuf() {
711        std::env::set_var("ENV_FLAGS_TEST_TYPES_PATHBUF", "/var/lib/file.txt");
712        env_flags! {
713            ENV_FLAGS_TEST_TYPES_PATHBUF: PathBuf;
714        }
715        assert_eq!(
716            *ENV_FLAGS_TEST_TYPES_PATHBUF,
717            PathBuf::from("/var/lib/file.txt")
718        );
719    }
720
721    #[test]
722    fn test_types_option() {
723        std::env::set_var("ENV_FLAGS_TEST_OPTION_SET", "cat");
724        env_flags! {
725            ENV_FLAGS_TEST_OPTION_UNSET: Option<&str> = None;
726            ENV_FLAGS_TEST_OPTION_SET: Option<&str> = None;
727        };
728        assert!(ENV_FLAGS_TEST_OPTION_UNSET.is_none());
729        assert_eq!(*ENV_FLAGS_TEST_OPTION_SET, Some("cat"));
730    }
731
732    #[test]
733    fn test_types_vec() {
734        std::env::set_var("ENV_FLAGS_TEST_VEC", "1,2,3,4");
735        env_flags! {
736            ENV_FLAGS_TEST_VEC: Vec<u32>;
737        };
738        assert_eq!(*ENV_FLAGS_TEST_VEC, vec![1, 2, 3, 4]);
739    }
740
741    #[test]
742    fn test_types_bool() {
743        std::env::set_var("ENV_FLAGS_TEST_BOOL_TRUE", "true");
744        std::env::set_var("ENV_FLAGS_TEST_BOOL_FALSE", "false");
745        std::env::set_var("ENV_FLAGS_TEST_BOOL_TRUE_UPPER", "TRUE");
746        std::env::set_var("ENV_FLAGS_TEST_BOOL_FALSE_UPPER", "FALSE");
747        std::env::set_var("ENV_FLAGS_TEST_BOOL_0", "0");
748        std::env::set_var("ENV_FLAGS_TEST_BOOL_1", "1");
749        env_flags! {
750            ENV_FLAGS_TEST_BOOL_TRUE: bool;
751            ENV_FLAGS_TEST_BOOL_FALSE: bool;
752            ENV_FLAGS_TEST_BOOL_TRUE_UPPER: bool;
753            ENV_FLAGS_TEST_BOOL_FALSE_UPPER: bool;
754            ENV_FLAGS_TEST_BOOL_0: bool;
755            ENV_FLAGS_TEST_BOOL_1: bool;
756        };
757        assert_eq!(*ENV_FLAGS_TEST_BOOL_TRUE, true);
758        assert_eq!(*ENV_FLAGS_TEST_BOOL_FALSE, false);
759        assert_eq!(*ENV_FLAGS_TEST_BOOL_TRUE_UPPER, true);
760        assert_eq!(*ENV_FLAGS_TEST_BOOL_FALSE_UPPER, false);
761        assert_eq!(*ENV_FLAGS_TEST_BOOL_0, false);
762        assert_eq!(*ENV_FLAGS_TEST_BOOL_1, true);
763    }
764
765    #[test]
766    fn test_deref() {
767        env_flags! {
768            ENV_FLAGS_TEST_DEREF: &str = "hello";
769        };
770
771        fn print_str(s: &str) {
772            println!("{}", s);
773        }
774
775        print_str(&ENV_FLAGS_TEST_DEREF);
776        print_str(*ENV_FLAGS_TEST_DEREF);
777    }
778
779    #[test]
780    fn test_debug() {
781        env_flags! {
782            ENV_FLAGS_TEST_DEBUG: &str = "cat";
783        };
784
785        let str = format!("{:?}", ENV_FLAGS_TEST_DEBUG);
786        assert_eq!(str, "\"cat\"");
787    }
788
789    #[test]
790    fn test_display() {
791        env_flags! {
792            ENV_FLAGS_TEST_DEBUG: &str = "cat";
793        };
794
795        let str = format!("{}", ENV_FLAGS_TEST_DEBUG);
796        assert_eq!(str, "cat");
797    }
798}