esp_config/generate/
validator.rs

1use std::{io::Write, ops::Range};
2
3use serde::{Deserialize, Serialize};
4
5use super::{Error, snake_case, value::Value};
6
7/// Configuration value validation functions.
8#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
9pub enum Validator {
10    /// Only allow negative integers, i.e. any values less than 0.
11    NegativeInteger,
12    /// Only allow non-negative integers, i.e. any values greater than or
13    /// equal to 0.
14    NonNegativeInteger,
15    /// Only allow positive integers, i.e. any values greater than to 0.
16    PositiveInteger,
17    /// Ensure that an integer value falls within the specified range.
18    IntegerInRange(Range<i128>),
19    /// String-Enumeration. Only allows one of the given Strings.
20    Enumeration(Vec<String>),
21}
22
23impl Validator {
24    /// Validate the value
25    pub fn validate(&self, value: &Value) -> Result<(), Error> {
26        match self {
27            Validator::NegativeInteger => negative_integer(value)?,
28            Validator::NonNegativeInteger => non_negative_integer(value)?,
29            Validator::PositiveInteger => positive_integer(value)?,
30            Validator::IntegerInRange(range) => integer_in_range(range, value)?,
31            Validator::Enumeration(values) => enumeration(values, value)?,
32        }
33
34        Ok(())
35    }
36
37    pub(crate) fn description(&self) -> Option<String> {
38        match self {
39            Validator::NegativeInteger => Some(String::from("Negative integer")),
40            Validator::NonNegativeInteger => Some(String::from("Positive integer or 0")),
41            Validator::PositiveInteger => Some(String::from("Positive integer")),
42            Validator::IntegerInRange(range) => {
43                Some(format!("Integer in range {}..{}", range.start, range.end))
44            }
45            Validator::Enumeration(values) => Some(format!(
46                "One of: <ul style=\"display: inline-block; text-align: left\">{}</ul>",
47                values
48                    .iter()
49                    .map(|v| format!("<li>{v}</li>"))
50                    .collect::<Vec<_>>()
51                    .join("")
52            )),
53        }
54    }
55
56    pub(crate) fn emit_cargo_extras(
57        &self,
58        mut stdout: impl Write,
59        config_key: &str,
60        actual_value: &Value,
61    ) {
62        if let Validator::Enumeration(values) = self {
63            for possible_value in values {
64                writeln!(
65                    stdout,
66                    "cargo:rustc-check-cfg=cfg({config_key}_{})",
67                    snake_case(possible_value)
68                )
69                .ok();
70            }
71
72            writeln!(
73                stdout,
74                "cargo:rustc-cfg={config_key}_{}",
75                snake_case(&actual_value.to_string())
76            )
77            .ok();
78        }
79    }
80}
81
82pub(crate) fn enumeration(values: &Vec<String>, value: &Value) -> Result<(), Error> {
83    if let Value::String(value) = value {
84        if !values.contains(value) {
85            return Err(Error::validation(format!(
86                "Expected one of {values:?}, found '{value}'"
87            )));
88        }
89
90        Ok(())
91    } else {
92        Err(Error::parse(
93            "Validator::Enumeration can only be used with string values",
94        ))
95    }
96}
97
98pub(crate) fn negative_integer(value: &Value) -> Result<(), Error> {
99    if !value.is_integer() {
100        return Err(Error::validation(
101            "Validator::NegativeInteger can only be used with integer values",
102        ));
103    } else if value.as_integer() >= 0 {
104        return Err(Error::validation(format!(
105            "Expected negative integer, found '{}'",
106            value.as_integer()
107        )));
108    }
109
110    Ok(())
111}
112
113pub(crate) fn non_negative_integer(value: &Value) -> Result<(), Error> {
114    if !value.is_integer() {
115        return Err(Error::validation(
116            "Validator::NonNegativeInteger can only be used with integer values",
117        ));
118    } else if value.as_integer() < 0 {
119        return Err(Error::validation(format!(
120            "Expected non-negative integer, found '{}'",
121            value.as_integer()
122        )));
123    }
124
125    Ok(())
126}
127
128pub(crate) fn positive_integer(value: &Value) -> Result<(), Error> {
129    if !value.is_integer() {
130        return Err(Error::validation(
131            "Validator::PositiveInteger can only be used with integer values",
132        ));
133    } else if value.as_integer() <= 0 {
134        return Err(Error::validation(format!(
135            "Expected positive integer, found '{}'",
136            value.as_integer()
137        )));
138    }
139
140    Ok(())
141}
142
143pub(crate) fn integer_in_range(range: &Range<i128>, value: &Value) -> Result<(), Error> {
144    if !value.is_integer() || !range.contains(&value.as_integer()) {
145        Err(Error::validation(format!(
146            "Value '{value}' does not fall within range '{range:?}'"
147        )))
148    } else {
149        Ok(())
150    }
151}