menv/
lib.rs

1//! # `menv`
2//! This crate provides [a macro](require_envs) for asserting the presence of a list of environment
3//! variables and accessing them as types which implement [`FromStr`](std::str::FromStr).
4
5use std::ops::{Deref, DerefMut};
6use std::str::FromStr;
7/// Generate the following:
8/// - A function which asserts the presence and well-formedness of a list of env vars
9/// - A function which returns a `bool` representing whether any of the required vars are set
10/// - A function which returns a `String` representing the collected help messages for the list of vars
11/// - A list of functions, one for each environment variable required, which parse and return the associated env var
12///
13/// # Example
14/// Here we fill an `env` module with required environment variables,
15/// print help and exit if none of them are set, runs the asserts for them
16/// if some are set, and proceeds.
17///
18/// Other parts of this program are now free, since the asserts were run at the start of main,
19/// to access `env::server_port()` and `env::db_path()` as if they are infallible.
20///
21/// The getter function name can be suffixed with `?` to make an env var optional. In this example,
22/// `plugin_dir`'s return type is `Option<String>`.
23///
24/// The getter function name can also, instead, be suffixed with `~` to make an env var use
25/// the [`Default`] value of its type when unset. In this example, [`Flag`]'s default value is `false`.
26/// ```
27/// mod env {
28///     use menv::require_envs;
29///     require_envs! {
30///         (assert_env_vars, any_set, gen_help);
31///
32///         server_port, "FERRISCRAFT_USERS_PORT", u16,
33///         "FERRISCRAFT_USERS_PORT should be set to the desired server port";
34///
35///         db_path, "FERRISCRAFT_USERS_DB", String,
36///         "FERRISCRAFT_USERS_DB should be set to the path to the users database";
37///
38///         plugin_dir?, "XLANG_PLUGIN_DIR", String,
39///         "XLANG_PLUGIN_DIR, if set, overrides the directory that lccc looks for xlang plugins";
40///
41///         do_overflow_checks~, "DO_OVERFLOW_CHECKS", Flag,
42///         "DO_OVERFLOW_CHECKS, if set, makes all additional overflow checks run";
43///     }
44/// }
45/// fn main() {
46///    if env::any_set() {
47///         env::assert_env_vars();
48///     } else {
49///         println!("# Environment Variables Help\n{}", env::gen_help());
50///         return
51///     }
52/// }
53/// ```
54#[macro_export]
55macro_rules! require_envs {
56    // We set a default visibility which is different from Rust's default to private.
57    (@func $fname:ident ?, $ename:literal, $ty:ty, $etext:literal) => {
58        $crate::require_envs! {@func pub $fname ?, $ename, $ty, $etext}
59    };
60    (@func $vis:vis $fname:ident ?, $ename:literal, $ty:ty, $etext:literal) => {
61        #[doc = $crate::__private::trimmed_help!($etext)]
62        $vis fn $fname() -> $crate::__private::Option<$ty> {
63            let x = $crate::__private::env::var($ename).ok();
64            x.and_then(|x| {
65                $crate::__private::Option::Some($crate::__private::FromStr::from_str(&x).expect($etext))
66            })
67        }
68    };
69    (@func $fname:ident ~, $ename:literal, $ty:ty, $etext:literal) => {
70        $crate::require_envs! {@func pub $fname ~, $ename, $ty, $etext}
71    };
72    (@func $vis:vis $fname:ident ~, $ename:literal, $ty:ty, $etext:literal) => {
73        #[doc = $crate::__private::trimmed_help!($etext)]
74        $vis fn $fname() -> $ty {
75            let x = $crate::__private::env::var($ename).ok();
76            let x = x.and_then(|x| {
77                $crate::__private::Option::Some($crate::__private::FromStr::from_str(&x).expect($etext))
78            });
79            x.unwrap_or_default()
80        }
81    };
82    (@func $fname:ident, $ename:literal, $ty:ty, $etext:literal) => {
83        $crate::require_envs! {@func pub $fname, $ename, $ty, $etext}
84    };
85    (@func $vis:vis $fname:ident, $ename:literal, $ty:ty, $etext:literal) => {
86        #[doc = $crate::__private::trimmed_help!($etext)]
87        $vis fn $fname() -> $ty {
88            $crate::__private::FromStr::from_str(&$crate::__private::env::var($ename).expect($etext)).expect($etext)
89        }
90    };
91    // We do not assert the existence of optional variables.
92    (@assert $vis:vis $fname:ident ?, $ename:literal, $ty:ty, $etext:literal) => {};
93    (@assert $vis:vis $fname:ident ~, $ename:literal, $ty:ty, $etext:literal) => {};
94    (@assert $vis:vis $fname:ident, $ename:literal, $ty:ty, $etext:literal) => {
95        let _ = $fname();
96    };
97    (@get_res $vis:vis $fname:ident $(?)? $(~)?, $ename:literal, $ty:ty, $etext:literal) => {
98        $crate::__private::env::var($ename)
99    };
100    (@etext $vis:vis $fname:ident $(?)? $(~)?, $ename:literal, $ty:ty, $etext:literal) => {
101        $etext
102    };
103    (($assert_name:ident, $any_set_name:ident, $help_name:ident); $($stream:tt)*) => {
104        // Note: While I now use a proc macro for dividing the input stream into declarations,
105        // the below comments still accurately describe what that proc macro generates invocations of.
106        pub fn $assert_name () {
107            $crate::__private::assert_var_body! {$crate $($stream)*}
108            // $(
109            //     $crate::require_envs! {@assert $a $b $c $d $e $f $g $($h)?}
110            // )*
111        }
112        pub fn $any_set_name() -> bool {
113            $crate::__private::any_set_body! {$crate $($stream)*}.iter().any(|x| x.is_ok())
114            // [$($crate::require_envs! {@get_res $a $b $c $d $e $f $g $($h)?}),*].iter().any(|x| x.is_ok())
115        }
116        pub fn $help_name() -> $crate::__private::String {
117            $crate::__private::help_body!{$crate $($stream)*}.iter().fold($crate::__private::String::new(), |a, x| {
118                a + x + "\n"
119            })
120            // $crate::__private::String::new() $(+ $crate::require_envs! {@etext $a $b $c $d $e $f $g $($h)?} + "\n")*
121        }
122        $crate::__private::getters! {$crate $($stream)*}
123        // $(
124        //     $crate::require_envs! {@func $a $b $c $d $e $f $g $($h)?}
125        // )*
126        $crate::__private::errors! {$crate $($stream)*}
127    }
128}
129
130/// Use this type instead of a [`bool`] if you want a var to
131/// evaluate to true if set to any value at all.
132///
133/// This is best used with the `~` getter modifier,
134/// which causes an unset var to use its type's [`Default`] implementation.
135#[derive(Default, Copy, Clone, Hash, Debug)]
136pub struct Flag {
137    pub val: bool,
138}
139impl FromStr for Flag {
140    type Err = ::core::convert::Infallible;
141    fn from_str(_: &str) -> Result<Self, Self::Err> {
142        Ok(Self { val: true })
143    }
144}
145impl Deref for Flag {
146    type Target = bool;
147    fn deref(&self) -> &Self::Target {
148        &self.val
149    }
150}
151impl DerefMut for Flag {
152    fn deref_mut(&mut self) -> &mut Self::Target {
153        &mut self.val
154    }
155}
156
157/// This module holds private re-exports which are used by [`require_envs`]
158/// to ensure it always refers to the right external items.
159#[doc(hidden)]
160pub mod __private {
161    pub use ::menv_proc_macro::{any_set_body, assert_var_body, errors, getters, help_body, trimmed_help};
162    pub use ::std::env;
163    pub use ::std::option::Option;
164    pub use ::std::str::FromStr;
165    pub use ::std::string::String;
166}