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}