svd_rs/
enumeratedvalue.rs

1use super::{BuildError, Description, EmptyToNone, Name, SvdError, ValidateLevel};
2
3/// Describes a single entry in the enumeration.
4#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
5#[derive(Clone, Debug, PartialEq, Eq)]
6#[non_exhaustive]
7pub struct EnumeratedValue {
8    /// String describing the semantics of the value. Can be displayed instead of the value
9    pub name: String,
10
11    /// Extended string describing the value
12    #[cfg_attr(
13        feature = "serde",
14        serde(default, skip_serializing_if = "Option::is_none")
15    )]
16    pub description: Option<String>,
17
18    /// Defines the constant for the bit-field as decimal, hexadecimal or binary number
19    #[cfg_attr(
20        feature = "serde",
21        serde(default, skip_serializing_if = "Option::is_none")
22    )]
23    pub value: Option<u64>,
24
25    /// Defines the name and description for all other values that are not listed explicitly
26    #[cfg_attr(
27        feature = "serde",
28        serde(default, skip_serializing_if = "Option::is_none")
29    )]
30    pub is_default: Option<bool>,
31}
32
33/// Errors for [`EnumeratedValue::validate`]
34#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
35pub enum Error {
36    /// No value was specified
37    #[error("EnumeratedValue has no `value` or `is_default`")]
38    AbsentValue,
39    /// Passed both value and isDefault, only checked in strict mode
40    #[error("EnumeratedValue with `value` (passed {0:?}) should not have `is_default(True)`")]
41    ValueAndDefault(Option<u64>),
42    /// The value is not in range.
43    #[error("Value {0} out of range [{} - {}]", .1.start, .1.end - 1)]
44    OutOfRange(u64, core::ops::Range<u64>),
45}
46
47/// Builder for [`EnumeratedValue`]
48#[derive(Clone, Debug, Default, PartialEq, Eq)]
49pub struct EnumeratedValueBuilder {
50    name: Option<String>,
51    description: Option<String>,
52    value: Option<u64>,
53    is_default: Option<bool>,
54}
55
56impl From<EnumeratedValue> for EnumeratedValueBuilder {
57    fn from(e: EnumeratedValue) -> Self {
58        Self {
59            name: Some(e.name),
60            description: e.description,
61            value: e.value,
62            is_default: e.is_default,
63        }
64    }
65}
66
67impl EnumeratedValueBuilder {
68    /// Set the name of the enumerated value.
69    pub fn name(mut self, value: String) -> Self {
70        self.name = Some(value);
71        self
72    }
73    /// Set the description of the enumerated value.
74    pub fn description(mut self, value: Option<String>) -> Self {
75        self.description = value;
76        self
77    }
78    /// Set the value of the enumerated value.
79    pub fn value(mut self, value: Option<u64>) -> Self {
80        self.value = value;
81        self
82    }
83    #[allow(clippy::wrong_self_convention)]
84    /// Set if the enumerated value is defaulted for non-explicit values.
85    pub fn is_default(mut self, value: Option<bool>) -> Self {
86        self.is_default = value;
87        self
88    }
89    /// Validate and build a [`EnumeratedValue`].
90    pub fn build(self, lvl: ValidateLevel) -> Result<EnumeratedValue, SvdError> {
91        let ev = EnumeratedValue {
92            name: self
93                .name
94                .ok_or_else(|| BuildError::Uninitialized("name".to_string()))?,
95            description: self.description.empty_to_none(),
96            value: self.value,
97            is_default: self.is_default,
98        };
99        ev.validate(lvl)?;
100        Ok(ev)
101    }
102}
103
104impl EnumeratedValue {
105    /// Enumerated value is defaulted for non-explicit values
106    pub fn is_default(&self) -> bool {
107        matches!(self.is_default, Some(true))
108    }
109    /// Make a builder for [`EnumeratedValue`]
110    pub fn builder() -> EnumeratedValueBuilder {
111        EnumeratedValueBuilder::default()
112    }
113    /// Modify an existing [`EnumeratedValue`] based on a [builder](EnumeratedValueBuilder).
114    pub fn modify_from(
115        &mut self,
116        builder: EnumeratedValueBuilder,
117        lvl: ValidateLevel,
118    ) -> Result<(), SvdError> {
119        if let Some(name) = builder.name {
120            self.name = name;
121        }
122        if builder.description.is_some() {
123            self.description = builder.description.empty_to_none();
124        }
125        if builder.value.is_some() {
126            self.value = builder.value;
127        }
128        if builder.is_default.is_some() {
129            self.is_default = builder.is_default;
130        }
131        self.validate(lvl)
132    }
133    /// Validate the [`EnumeratedValue`].
134    pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> {
135        if !lvl.is_disabled() {
136            if lvl.is_strict() {
137                super::check_name(&self.name, "name")?;
138            }
139            match (self.value.is_some(), self.is_default()) {
140                (false, false) => Err(Error::AbsentValue.into()),
141                (true, true) if lvl.is_strict() => Err(Error::ValueAndDefault(self.value).into()),
142                _ => Ok(()),
143            }
144        } else {
145            Ok(())
146        }
147    }
148    pub(crate) fn check_range(&self, range: &core::ops::Range<u64>) -> Result<(), SvdError> {
149        match &self.value {
150            Some(x) if !range.contains(x) => Err(Error::OutOfRange(*x, range.clone()).into()),
151            _ => Ok(()),
152        }
153    }
154}
155
156impl Name for EnumeratedValue {
157    fn name(&self) -> &str {
158        &self.name
159    }
160}
161
162impl Description for EnumeratedValue {
163    fn description(&self) -> Option<&str> {
164        self.description.as_deref()
165    }
166}