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