esp_config/
lib.rs

1#![doc = include_str!("../README.md")]
2//! ## Feature Flags
3#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
4#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
5#![cfg_attr(not(feature = "build"), no_std)]
6#![deny(missing_docs, rust_2018_idioms, rustdoc::all)]
7
8#[cfg(feature = "build")]
9mod generate;
10#[cfg(feature = "build")]
11pub use generate::{generate_config, Error, Validator, Value};
12
13/// Parse the value of an environment variable as a [bool] at compile time.
14#[macro_export]
15macro_rules! esp_config_bool {
16    ( $var:expr ) => {
17        match env!($var).as_bytes() {
18            b"true" => true,
19            b"false" => false,
20            _ => ::core::panic!("boolean value must be either 'true' or 'false'"),
21        }
22    };
23}
24
25// TODO: From 1.82 on, we can use `<$ty>::from_str_radix(env!($var), 10)`
26/// Parse the value of an environment variable as an integer at compile time.
27#[macro_export]
28macro_rules! esp_config_int {
29    ( $ty:ty, $var:expr ) => {
30        const {
31            const BYTES: &[u8] = env!($var).as_bytes();
32            $crate::esp_config_int_parse!($ty, BYTES)
33        }
34    };
35}
36
37/// Get the string value of an environment variable at compile time.
38#[macro_export]
39macro_rules! esp_config_str {
40    ( $var:expr ) => {
41        env!($var)
42    };
43}
44
45/// Parse a string like "777" into an integer, which _can_ be used in a `const`
46/// context
47#[doc(hidden)] // To avoid confusion with `esp_config_int`, hide this in the docs
48#[macro_export]
49macro_rules! esp_config_int_parse {
50    ( $ty:ty, $bytes:expr ) => {{
51        let mut bytes = $bytes;
52        let mut val: $ty = 0;
53        let mut sign_seen = false;
54        let mut is_negative = false;
55
56        while let [byte, rest @ ..] = bytes {
57            match *byte {
58                b'0'..=b'9' => {
59                    val = val * 10 + (*byte - b'0') as $ty;
60                }
61                b'-' | b'+' if !sign_seen => {
62                    is_negative = *byte == b'-';
63                    sign_seen = true;
64                }
65                _ => ::core::panic!("invalid character encountered while parsing integer"),
66            }
67
68            bytes = rest;
69        }
70
71        if is_negative {
72            let original = val;
73            // Subtract the value twice to get a negative:
74            val -= original;
75            val -= original;
76        }
77
78        val
79    }};
80}
81
82#[cfg(test)]
83mod test {
84    // We can only test success in the const context
85    const _: () = {
86        core::assert!(esp_config_int_parse!(i64, "-77777".as_bytes()) == -77777);
87        core::assert!(esp_config_int_parse!(isize, "-7777".as_bytes()) == -7777);
88        core::assert!(esp_config_int_parse!(i32, "-999".as_bytes()) == -999);
89        core::assert!(esp_config_int_parse!(i16, "-99".as_bytes()) == -99);
90        core::assert!(esp_config_int_parse!(i8, "-9".as_bytes()) == -9);
91
92        core::assert!(esp_config_int_parse!(u64, "77777".as_bytes()) == 77777);
93        core::assert!(esp_config_int_parse!(usize, "7777".as_bytes()) == 7777);
94        core::assert!(esp_config_int_parse!(u32, "999".as_bytes()) == 999);
95        core::assert!(esp_config_int_parse!(u16, "99".as_bytes()) == 99);
96        core::assert!(esp_config_int_parse!(u8, "9".as_bytes()) == 9);
97    };
98
99    #[test]
100    #[should_panic]
101    fn test_expect_positive() {
102        esp_config_int_parse!(u8, "-5".as_bytes());
103    }
104
105    #[test]
106    #[should_panic]
107    fn test_invalid_digit() {
108        esp_config_int_parse!(u32, "a".as_bytes());
109    }
110}