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