svd_rs/
dimelement.rs

1use super::{BuildError, EmptyToNone, EnumeratedValue, SvdError, ValidateLevel};
2use std::borrow::Cow;
3use std::ops::RangeInclusive;
4
5/// Defines arrays and lists.
6#[cfg_attr(
7    feature = "serde",
8    derive(serde::Deserialize, serde::Serialize),
9    serde(rename_all = "camelCase")
10)]
11#[derive(Clone, Debug, PartialEq, Eq)]
12#[non_exhaustive]
13pub struct DimElement {
14    /// Defines the number of elements in an array or list
15    pub dim: u32,
16
17    /// Specify the address increment between two neighboring array or list members in the address map
18    pub dim_increment: u32,
19
20    /// Specify the strings that substitue the placeholder `%s` within `name` and `displayName`.
21    /// By default, <dimIndex> is a value starting with 0
22    #[cfg_attr(
23        feature = "serde",
24        serde(
25            deserialize_with = "ser_de::deserialize_dim_index",
26            serialize_with = "ser_de::serialize_dim_index",
27            skip_serializing_if = "Option::is_none"
28        )
29    )]
30    pub dim_index: Option<Vec<String>>,
31
32    /// Specify the name of the structure. If not defined, then the entry of the `name` element is used
33    #[cfg_attr(
34        feature = "serde",
35        serde(default, skip_serializing_if = "Option::is_none")
36    )]
37    pub dim_name: Option<String>,
38
39    /// Grouping element to create enumerations in the header file
40    #[cfg_attr(
41        feature = "serde",
42        serde(default, skip_serializing_if = "Option::is_none")
43    )]
44    pub dim_array_index: Option<DimArrayIndex>,
45}
46
47/// Grouping element to create enumerations in the header file
48///
49/// This information is used for generating an enum in the device header file.
50/// The debugger may use this information to display the identifier string
51/// as well as the description. Just like symbolic constants making source
52/// code more readable, the system view in the debugger becomes more instructive
53#[cfg_attr(
54    feature = "serde",
55    derive(serde::Deserialize, serde::Serialize),
56    serde(rename_all = "camelCase")
57)]
58#[derive(Clone, Debug, PartialEq, Eq)]
59pub struct DimArrayIndex {
60    /// Specify the base name of enumerations
61    #[cfg_attr(
62        feature = "serde",
63        serde(default, skip_serializing_if = "Option::is_none")
64    )]
65    pub header_enum_name: Option<String>,
66
67    /// Specify the values contained in the enumeration
68    pub values: Vec<EnumeratedValue>,
69}
70
71/// Builder for [`DimElement`]
72#[derive(Clone, Debug, Default, PartialEq, Eq)]
73pub struct DimElementBuilder {
74    dim: Option<u32>,
75    dim_increment: Option<u32>,
76    dim_index: Option<Vec<String>>,
77    dim_name: Option<String>,
78    dim_array_index: Option<DimArrayIndex>,
79}
80
81impl From<DimElement> for DimElementBuilder {
82    fn from(d: DimElement) -> Self {
83        Self {
84            dim: Some(d.dim),
85            dim_increment: Some(d.dim_increment),
86            dim_index: d.dim_index,
87            dim_name: d.dim_name,
88            dim_array_index: d.dim_array_index,
89        }
90    }
91}
92
93impl DimElementBuilder {
94    /// Set the dim of the elements
95    pub fn dim(mut self, value: u32) -> Self {
96        self.dim = Some(value);
97        self
98    }
99    /// Set the dim increment of the elements
100    pub fn dim_increment(mut self, value: u32) -> Self {
101        self.dim_increment = Some(value);
102        self
103    }
104    /// Set the dim index of the elements
105    pub fn dim_index(mut self, value: Option<Vec<String>>) -> Self {
106        self.dim_index = value;
107        self
108    }
109    /// Set the dim name of the elements
110    pub fn dim_name(mut self, value: Option<String>) -> Self {
111        self.dim_name = value;
112        self
113    }
114    /// Set the dim_array_index of the elements
115    pub fn dim_array_index(mut self, value: Option<DimArrayIndex>) -> Self {
116        self.dim_array_index = value;
117        self
118    }
119    /// Validate and build a [`DimElement`].
120    pub fn build(self, lvl: ValidateLevel) -> Result<DimElement, SvdError> {
121        let de = DimElement {
122            dim: self
123                .dim
124                .ok_or_else(|| BuildError::Uninitialized("dim".to_string()))?,
125            dim_increment: self
126                .dim_increment
127                .ok_or_else(|| BuildError::Uninitialized("dim_increment".to_string()))?,
128            dim_index: self.dim_index.empty_to_none(),
129            dim_name: self.dim_name.empty_to_none(),
130            dim_array_index: self.dim_array_index,
131        };
132        de.validate(lvl)?;
133        Ok(de)
134    }
135}
136
137impl DimElement {
138    /// Make a builder for [`DimElement`]
139    pub fn builder() -> DimElementBuilder {
140        DimElementBuilder::default()
141    }
142
143    /// Get array of indexes from string
144    pub fn parse_indexes(text: &str) -> Option<Vec<String>> {
145        (if text.contains('-') {
146            let (start, end) = text.split_once('-')?;
147            if let (Ok(start), Ok(end)) = (start.parse::<u32>(), end.parse::<u32>()) {
148                Some((start..=end).map(|i| i.to_string()).collect::<Vec<_>>())
149            } else {
150                let mut start = start.bytes();
151                let mut end = end.bytes();
152                match (start.next(), start.next(), end.next(), end.next()) {
153                    (Some(start), None, Some(end), None)
154                        if (start.is_ascii_lowercase() && end.is_ascii_lowercase())
155                            || (start.is_ascii_uppercase() && end.is_ascii_uppercase()) =>
156                    {
157                        Some((start..=end).map(|c| char::from(c).to_string()).collect())
158                    }
159                    _ => None,
160                }
161            }
162        } else {
163            Some(text.split(',').map(|s| s.to_string()).collect())
164        })
165        .filter(|v| !v.is_empty())
166    }
167    /// Try to represent [`DimElement`] as range of integer indexes
168    pub fn indexes_as_range(&self) -> Option<RangeInclusive<u32>> {
169        let mut integers = Vec::with_capacity(self.dim as usize);
170        for idx in self.indexes() {
171            // XXX: indexes that begin with leading zero are not compatible with range (`0-x`) syntax in serialization
172            // see https://github.com/rust-embedded/svdtools/pull/178#issuecomment-1801433808
173            let val = idx.parse::<u32>().ok()?;
174            if val.to_string() != idx {
175                return None;
176            }
177            integers.push(val);
178        }
179        let min = *integers.iter().min()?;
180        let max = *integers.iter().max()?;
181        if max - min + 1 != self.dim {
182            return None;
183        }
184        for (&i, r) in integers.iter().zip(min..=max) {
185            if i != r {
186                return None;
187            }
188        }
189        Some(min..=max)
190    }
191    /// Modify an existing [`DimElement`] based on a [builder](DimElementBuilder).
192    pub fn modify_from(
193        &mut self,
194        builder: DimElementBuilder,
195        lvl: ValidateLevel,
196    ) -> Result<(), SvdError> {
197        if let Some(dim) = builder.dim {
198            self.dim = dim;
199        }
200        if let Some(dim_increment) = builder.dim_increment {
201            self.dim_increment = dim_increment;
202        }
203        if builder.dim_index.is_some() {
204            self.dim_index = builder.dim_index.empty_to_none();
205        }
206        if builder.dim_name.is_some() {
207            self.dim_name = builder.dim_name.empty_to_none();
208        }
209        if builder.dim_array_index.is_some() {
210            self.dim_array_index = builder.dim_array_index;
211        }
212        self.validate(lvl)
213    }
214    /// Validate the [`DimElement`].
215    ///
216    /// # Notes
217    ///
218    /// This doesn't do anything.
219    pub fn validate(&self, _lvl: ValidateLevel) -> Result<(), SvdError> {
220        // TODO
221        Ok(())
222    }
223    /// Get the indexes of the array or list.
224    pub fn indexes(&self) -> Indexes {
225        Indexes {
226            i: 0,
227            dim: self.dim,
228            dim_index: &self.dim_index,
229        }
230    }
231}
232
233/// Indexes into a [DimElement]
234pub struct Indexes<'a> {
235    i: u32,
236    dim: u32,
237    dim_index: &'a Option<Vec<String>>,
238}
239
240impl<'a> Iterator for Indexes<'a> {
241    type Item = Cow<'a, str>;
242    fn next(&mut self) -> Option<Self::Item> {
243        if self.i == self.dim {
244            return None;
245        }
246        let i = self.i;
247        self.i += 1;
248        if let Some(index) = self.dim_index.as_ref() {
249            Some(index[i as usize].as_str().into())
250        } else {
251            Some(i.to_string().into())
252        }
253    }
254}
255
256#[cfg(feature = "serde")]
257mod ser_de {
258    use super::*;
259    use serde::{de, Deserialize, Deserializer, Serializer};
260    #[derive(serde::Serialize, serde::Deserialize)]
261    #[serde(untagged)]
262    enum DimIndex {
263        Array(Vec<String>),
264        String(String),
265    }
266
267    pub fn deserialize_dim_index<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
268    where
269        D: Deserializer<'de>,
270    {
271        Ok(match Option::<DimIndex>::deserialize(deserializer)? {
272            None => None,
273            Some(DimIndex::Array(a)) => Some(a),
274            Some(DimIndex::String(s)) => Some(
275                DimElement::parse_indexes(&s)
276                    .ok_or_else(|| de::Error::custom("Failed to deserialize dimIndex"))?,
277            ),
278        })
279    }
280
281    pub fn serialize_dim_index<S>(val: &Option<Vec<String>>, s: S) -> Result<S::Ok, S::Error>
282    where
283        S: Serializer,
284    {
285        s.serialize_str(&val.as_ref().unwrap().join(","))
286    }
287}