mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 12:50:53 +00:00
esp-config: allow negative values (#2204)
* esp-config: allow negative values * Add small test for parsing erroneous negative values * split Value::Number into signed and unsigned
This commit is contained in:
parent
26fc8871db
commit
117327e206
@ -15,7 +15,8 @@ pub struct ParseError(String);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Value {
|
||||
Number(usize),
|
||||
SignedInteger(isize),
|
||||
UnsignedInteger(usize),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
@ -23,19 +24,28 @@ pub enum Value {
|
||||
impl Value {
|
||||
fn parse_in_place(&mut self, s: &str) -> Result<(), ParseError> {
|
||||
*self = match self {
|
||||
Value::Bool(_) => match s {
|
||||
"false" | "no" | "n" => Value::Bool(false),
|
||||
"true" | "yes" | "y" => Value::Bool(true),
|
||||
Value::Bool(_) => match s.to_lowercase().as_ref() {
|
||||
"false" | "no" | "n" | "f" => Value::Bool(false),
|
||||
"true" | "yes" | "y" | "t" => Value::Bool(true),
|
||||
_ => return Err(ParseError(format!("Invalid boolean value: {}", s))),
|
||||
},
|
||||
Value::Number(_) => Value::Number(
|
||||
Value::SignedInteger(_) => Value::SignedInteger(
|
||||
match s.as_bytes() {
|
||||
[b'0', b'x', ..] => isize::from_str_radix(&s[2..], 16),
|
||||
[b'0', b'o', ..] => isize::from_str_radix(&s[2..], 8),
|
||||
[b'0', b'b', ..] => isize::from_str_radix(&s[2..], 2),
|
||||
_ => isize::from_str_radix(&s, 10),
|
||||
}
|
||||
.map_err(|_| ParseError(format!("Invalid signed numerical value: {}", s)))?,
|
||||
),
|
||||
Value::UnsignedInteger(_) => Value::UnsignedInteger(
|
||||
match s.as_bytes() {
|
||||
[b'0', b'x', ..] => usize::from_str_radix(&s[2..], 16),
|
||||
[b'0', b'o', ..] => usize::from_str_radix(&s[2..], 8),
|
||||
[b'0', b'b', ..] => usize::from_str_radix(&s[2..], 2),
|
||||
_ => usize::from_str_radix(&s, 10),
|
||||
}
|
||||
.map_err(|_| ParseError(format!("Invalid numerical value: {}", s)))?,
|
||||
.map_err(|_| ParseError(format!("Invalid unsigned numerical value: {}", s)))?,
|
||||
),
|
||||
Value::String(_) => Value::String(String::from(s)),
|
||||
};
|
||||
@ -45,7 +55,8 @@ impl Value {
|
||||
fn as_string(&self) -> String {
|
||||
match self {
|
||||
Value::Bool(value) => String::from(if *value { "true" } else { "false" }),
|
||||
Value::Number(value) => format!("{}", value),
|
||||
Value::SignedInteger(value) => format!("{}", value),
|
||||
Value::UnsignedInteger(value) => format!("{}", value),
|
||||
Value::String(value) => value.clone(),
|
||||
}
|
||||
}
|
||||
@ -242,7 +253,7 @@ mod test {
|
||||
#[test]
|
||||
fn value_number_formats() {
|
||||
const INPUTS: &[&str] = &["0xAA", "0o252", "0b0000000010101010", "170"];
|
||||
let mut v = Value::Number(0);
|
||||
let mut v = Value::SignedInteger(0);
|
||||
|
||||
for input in INPUTS {
|
||||
v.parse_in_place(input).unwrap();
|
||||
@ -275,6 +286,7 @@ mod test {
|
||||
temp_env::with_vars(
|
||||
[
|
||||
("ESP_TEST_NUMBER", Some("0xaa")),
|
||||
("ESP_TEST_NUMBER_SIGNED", Some("-999")),
|
||||
("ESP_TEST_STRING", Some("Hello world!")),
|
||||
("ESP_TEST_BOOL", Some("true")),
|
||||
],
|
||||
@ -282,10 +294,11 @@ mod test {
|
||||
let configs = generate_config(
|
||||
"esp-test",
|
||||
&[
|
||||
("number", Value::Number(999), "NA"),
|
||||
("number", Value::UnsignedInteger(999), "NA"),
|
||||
("number_signed", Value::SignedInteger(-777), "NA"),
|
||||
("string", Value::String("Demo".to_owned()), "NA"),
|
||||
("bool", Value::Bool(false), "NA"),
|
||||
("number_default", Value::Number(999), "NA"),
|
||||
("number_default", Value::UnsignedInteger(999), "NA"),
|
||||
("string_default", Value::String("Demo".to_owned()), "NA"),
|
||||
("bool_default", Value::Bool(false), "NA"),
|
||||
],
|
||||
@ -295,11 +308,18 @@ mod test {
|
||||
// some values have changed
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_NUMBER").unwrap() {
|
||||
Value::Number(num) => *num,
|
||||
Value::UnsignedInteger(num) => *num,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
0xaa
|
||||
);
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_NUMBER_SIGNED").unwrap() {
|
||||
Value::SignedInteger(num) => *num,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
-999
|
||||
);
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_STRING").unwrap() {
|
||||
Value::String(val) => val,
|
||||
@ -318,7 +338,7 @@ mod test {
|
||||
// the rest are the defaults
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_NUMBER_DEFAULT").unwrap() {
|
||||
Value::Number(num) => *num,
|
||||
Value::UnsignedInteger(num) => *num,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
999
|
||||
@ -350,7 +370,11 @@ mod test {
|
||||
("ESP_TEST_RANDOM_VARIABLE", Some("")),
|
||||
],
|
||||
|| {
|
||||
generate_config("esp-test", &[("number", Value::Number(999), "NA")], false);
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[("number", Value::UnsignedInteger(999), "NA")],
|
||||
false,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -359,7 +383,11 @@ mod test {
|
||||
#[should_panic]
|
||||
fn env_invalid_values_bails() {
|
||||
temp_env::with_vars([("ESP_TEST_NUMBER", Some("Hello world"))], || {
|
||||
generate_config("esp-test", &[("number", Value::Number(999), "NA")], false);
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[("number", Value::UnsignedInteger(999), "NA")],
|
||||
false,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,22 +11,18 @@ pub use generate::*;
|
||||
|
||||
#[macro_export]
|
||||
// TODO from 1.82 we can use <$ty>::from_str_radix(env!($var), 10) instead
|
||||
/// Parse the value of a `env` variable as an integer at compile time
|
||||
macro_rules! esp_config_int {
|
||||
($ty:ty, $var:expr) => {
|
||||
const {
|
||||
let mut bytes = env!($var).as_bytes();
|
||||
let mut val: $ty = 0;
|
||||
while let [byte, rest @ ..] = bytes {
|
||||
::core::assert!(b'0' <= *byte && *byte <= b'9', "invalid digit");
|
||||
val = val * 10 + (*byte - b'0') as $ty;
|
||||
bytes = rest;
|
||||
}
|
||||
val
|
||||
const BYTES: &[u8] = env!($var).as_bytes();
|
||||
esp_config_int_parse!($ty, BYTES)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Get the string value of an `env` variable at compile time
|
||||
macro_rules! esp_config_str {
|
||||
($var:expr) => {
|
||||
env!($var)
|
||||
@ -34,6 +30,7 @@ macro_rules! esp_config_str {
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Parse the value of a `env` variable as an bool at compile time
|
||||
macro_rules! esp_config_bool {
|
||||
($var:expr) => {
|
||||
match env!($var).as_bytes() {
|
||||
@ -42,3 +39,63 @@ macro_rules! esp_config_bool {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)] // to avoid confusion with esp_config_int, let's hide this
|
||||
/// Parse a string like "777" into an integer, which _can_ be used in a `const`
|
||||
/// context
|
||||
macro_rules! esp_config_int_parse {
|
||||
($ty:ty, $bytes:expr) => {{
|
||||
let mut bytes = $bytes;
|
||||
let mut val: $ty = 0;
|
||||
let mut sign_seen = false;
|
||||
let mut is_negative = false;
|
||||
while let [byte, rest @ ..] = bytes {
|
||||
match *byte {
|
||||
b'0'..=b'9' => {
|
||||
val = val * 10 + (*byte - b'0') as $ty;
|
||||
}
|
||||
b'-' | b'+' if !sign_seen => {
|
||||
if *byte == b'-' {
|
||||
is_negative = true;
|
||||
}
|
||||
sign_seen = true;
|
||||
}
|
||||
_ => ::core::panic!("invalid digit"),
|
||||
}
|
||||
bytes = rest;
|
||||
}
|
||||
if is_negative {
|
||||
let original = val;
|
||||
// subtract twice to get the negative
|
||||
val -= original;
|
||||
val -= original;
|
||||
}
|
||||
val
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
// We can only test success in the const context
|
||||
const _: () = {
|
||||
core::assert!(esp_config_int_parse!(i64, "-77777".as_bytes()) == -77777);
|
||||
core::assert!(esp_config_int_parse!(isize, "-7777".as_bytes()) == -7777);
|
||||
core::assert!(esp_config_int_parse!(i32, "-999".as_bytes()) == -999);
|
||||
core::assert!(esp_config_int_parse!(i16, "-99".as_bytes()) == -99);
|
||||
core::assert!(esp_config_int_parse!(i8, "-9".as_bytes()) == -9);
|
||||
|
||||
core::assert!(esp_config_int_parse!(u64, "77777".as_bytes()) == 77777);
|
||||
core::assert!(esp_config_int_parse!(usize, "7777".as_bytes()) == 7777);
|
||||
core::assert!(esp_config_int_parse!(u32, "999".as_bytes()) == 999);
|
||||
core::assert!(esp_config_int_parse!(u16, "99".as_bytes()) == 99);
|
||||
core::assert!(esp_config_int_parse!(u8, "9".as_bytes()) == 9);
|
||||
};
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_expect_positive() {
|
||||
esp_config_int_parse!(u8, "-5".as_bytes());
|
||||
}
|
||||
}
|
||||
|
@ -96,17 +96,17 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
generate_config(
|
||||
"esp_wifi",
|
||||
&[
|
||||
("rx_queue_size", Value::Number(5), "Size of the RX queue in frames"),
|
||||
("tx_queue_size", Value::Number(3), "Size of the TX queue in frames"),
|
||||
("static_rx_buf_num", Value::Number(10), "WiFi static RX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("dynamic_rx_buf_num", Value::Number(32), "WiFi dynamic RX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("static_tx_buf_num", Value::Number(0), "WiFi static TX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("dynamic_tx_buf_num", Value::Number(32), "WiFi dynamic TX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("rx_queue_size", Value::UnsignedInteger(5), "Size of the RX queue in frames"),
|
||||
("tx_queue_size", Value::UnsignedInteger(3), "Size of the TX queue in frames"),
|
||||
("static_rx_buf_num", Value::UnsignedInteger(10), "WiFi static RX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("dynamic_rx_buf_num", Value::UnsignedInteger(32), "WiFi dynamic RX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("static_tx_buf_num", Value::UnsignedInteger(0), "WiFi static TX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("dynamic_tx_buf_num", Value::UnsignedInteger(32), "WiFi dynamic TX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("ampdu_rx_enable", Value::Bool(false), "WiFi AMPDU RX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("ampdu_tx_enable", Value::Bool(false), "WiFi AMPDU TX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("amsdu_tx_enable", Value::Bool(false), "WiFi AMSDU TX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("rx_ba_win", Value::Number(6), "WiFi Block Ack RX window size. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("max_burst_size", Value::Number(1), "See [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_burst_size)"),
|
||||
("rx_ba_win", Value::UnsignedInteger(6), "WiFi Block Ack RX window size. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"),
|
||||
("max_burst_size", Value::UnsignedInteger(1), "See [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_burst_size)"),
|
||||
(
|
||||
"country_code",
|
||||
Value::String("CN".to_owned()),
|
||||
@ -114,16 +114,16 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
),
|
||||
(
|
||||
"country_code_operating_class",
|
||||
Value::Number(0),
|
||||
Value::UnsignedInteger(0),
|
||||
"If not 0: Operating Class table number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)",
|
||||
),
|
||||
("mtu", Value::Number(1492), "MTU, see [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_transmission_unit)"),
|
||||
("tick_rate_hz", Value::Number(100), "Tick rate of the internal task scheduler in hertz"),
|
||||
("listen_interval", Value::Number(3), "Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen to beacon is 300 ms"),
|
||||
("beacon_timeout", Value::Number(6), "For Station, If the station does not receive a beacon frame from the connected SoftAP during the inactive time, disconnect from SoftAP. Default 6s. Range 6-30"),
|
||||
("ap_beacon_timeout", Value::Number(300), "For SoftAP, If the SoftAP doesn’t receive any data from the connected STA during inactive time, the SoftAP will force deauth the STA. Default is 300s"),
|
||||
("failure_retry_cnt", Value::Number(1), "Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config. Note: Enabling this may cause connection time to increase incase best AP doesn't behave properly. Defaults to 1"),
|
||||
("scan_method", Value::Number(0), "0 = WIFI_FAST_SCAN, 1 = WIFI_ALL_CHANNEL_SCAN, defaults to 0"),
|
||||
("mtu", Value::UnsignedInteger(1492), "MTU, see [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_transmission_unit)"),
|
||||
("tick_rate_hz", Value::UnsignedInteger(100), "Tick rate of the internal task scheduler in hertz"),
|
||||
("listen_interval", Value::UnsignedInteger(3), "Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen to beacon is 300 ms"),
|
||||
("beacon_timeout", Value::UnsignedInteger(6), "For Station, If the station does not receive a beacon frame from the connected SoftAP during the inactive time, disconnect from SoftAP. Default 6s. Range 6-30"),
|
||||
("ap_beacon_timeout", Value::UnsignedInteger(300), "For SoftAP, If the SoftAP doesn’t receive any data from the connected STA during inactive time, the SoftAP will force deauth the STA. Default is 300s"),
|
||||
("failure_retry_cnt", Value::UnsignedInteger(1), "Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config. Note: Enabling this may cause connection time to increase incase best AP doesn't behave properly. Defaults to 1"),
|
||||
("scan_method", Value::UnsignedInteger(0), "0 = WIFI_FAST_SCAN, 1 = WIFI_ALL_CHANNEL_SCAN, defaults to 0"),
|
||||
],
|
||||
true
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user