Skip to main content

cmsis_pdsc_parser/
conditions.rs

1//! Contains the types required to represent a [PDSC Conditions](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html#element_conditions) element
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
6/// Represents the [PDSC conditions](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html#element_conditions) element
7///
8/// Groups all condition definitions used to conditionally include components, files, and other pack content.
9pub struct Conditions {
10    /// Condition definitions (1..*)
11    #[serde(rename = "condition", default)]
12    pub conditions: Vec<Condition>,
13}
14
15#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
16/// Represents a [PDSC condition](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html#element_condition) element
17///
18/// A condition groups `accept`, `require`, and `deny` filter rules identified by a unique `id`.
19pub struct Condition {
20    /// Unique identifier for this condition within the pack
21    pub id: String,
22
23    /// Human-readable explanation of what the condition tests
24    pub description: Option<String>,
25
26    /// OR-rules: at least one `accept` filter must evaluate to true
27    #[serde(rename = "accept", default)]
28    pub accept: Vec<Filter>,
29
30    /// AND-rules: all `require` filters must evaluate to true
31    #[serde(rename = "require", default)]
32    pub require: Vec<Filter>,
33
34    /// AND-NOT-rules: no `deny` filter may evaluate to true
35    #[serde(rename = "deny", default)]
36    pub deny: Vec<Filter>,
37}
38
39#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
40#[allow(clippy::too_long_first_doc_paragraph)]
41/// Represents the shared filter attributes used by
42/// [`<accept>`](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html#element_accept),
43/// [`<require>`](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html#element_require), and
44/// [`<deny>`](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html#element_deny) elements
45///
46/// All attributes are optional; at least one must be set in a valid filter.
47pub struct Filter {
48    /// Device vendor (e.g. `ARM:82`, `STMicroelectronics:13`)
49    #[serde(rename = "Dvendor")]
50    pub device_vendor: Option<String>,
51
52    /// Device name; supports wildcards `*`, `?`, `[abc]`
53    #[serde(rename = "Dname")]
54    pub device_name: Option<String>,
55
56    /// Processor name for multi-core devices
57    #[serde(rename = "Pname")]
58    pub processor_name: Option<String>,
59
60    /// Processor core (e.g. `Cortex-M0`, `Cortex-M4`)
61    #[serde(rename = "Dcore")]
62    pub device_core: Option<String>,
63
64    /// Floating-point unit type
65    #[serde(rename = "Dfpu")]
66    pub fpu: Option<String>,
67
68    /// Memory protection unit presence
69    #[serde(rename = "Dmpu")]
70    pub mpu: Option<String>,
71
72    /// TrustZone support
73    #[serde(rename = "Dtz")]
74    pub trustzone: Option<String>,
75
76    /// Secure state support
77    #[serde(rename = "Dsecure")]
78    pub secure: Option<String>,
79
80    /// DSP instruction set support
81    #[serde(rename = "Ddsp")]
82    pub dsp: Option<String>,
83
84    /// M-Profile Vector Extension (Helium) support
85    #[serde(rename = "Dmve")]
86    pub mve: Option<String>,
87
88    /// Pointer Authentication and Branch Target Identification support
89    #[serde(rename = "Dpacbti")]
90    pub pacbti: Option<String>,
91
92    /// Byte endianness (e.g. `Little-endian`, `Big-endian`)
93    #[serde(rename = "Dendian")]
94    pub endian: Option<String>,
95
96    /// Component vendor
97    #[serde(rename = "Cvendor")]
98    pub component_vendor: Option<String>,
99
100    /// Component bundle name
101    #[serde(rename = "Cbundle")]
102    pub bundle: Option<String>,
103
104    /// Component class
105    #[serde(rename = "Cclass")]
106    pub class: Option<String>,
107
108    /// Component group
109    #[serde(rename = "Cgroup")]
110    pub group: Option<String>,
111
112    /// Component sub-group
113    #[serde(rename = "Csub")]
114    pub sub: Option<String>,
115
116    /// Component variant
117    #[serde(rename = "Cvariant")]
118    pub variant: Option<String>,
119
120    /// Component version range
121    #[serde(rename = "Cversion")]
122    pub version: Option<String>,
123
124    /// API version range
125    #[serde(rename = "Capiversion")]
126    pub api_version: Option<String>,
127
128    /// Board vendor
129    #[serde(rename = "Bvendor")]
130    pub board_vendor: Option<String>,
131
132    /// Board name
133    #[serde(rename = "Bname")]
134    pub board_name: Option<String>,
135
136    /// Board revision
137    #[serde(rename = "Brevision")]
138    pub board_revision: Option<String>,
139
140    /// Hardware part vendor
141    #[serde(rename = "Hvendor")]
142    pub part_vendor: Option<String>,
143
144    /// Hardware part name
145    #[serde(rename = "Hname")]
146    pub part_name: Option<String>,
147
148    /// Compiler toolchain (e.g. `GCC`, `ARMCC`, `IAR`)
149    #[serde(rename = "Tcompiler")]
150    pub compiler: Option<String>,
151
152    /// Compiler options or mode (e.g. `AC5`, `AC6`)
153    #[serde(rename = "Toptions")]
154    pub compiler_options: Option<String>,
155
156    /// Compiler output type (e.g. `exe`, `lib`)
157    #[serde(rename = "Toutput")]
158    pub compiler_output: Option<String>,
159
160    /// References another condition by its `id`; this filter is true only if that condition is true
161    pub condition: Option<String>,
162
163    /// Deprecated since v1.5; use [`device_name`](Self::device_name) with the family prefix instead; see [pdsc_conditions_pg](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html)
164    #[serde(rename = "Dfamily")]
165    pub d_family: Option<String>,
166
167    /// Deprecated since v1.5; use [DsubFamily](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html)
168    #[serde(rename = "DsubFamily")]
169    pub d_sub_family: Option<String>,
170
171    /// Deprecated since v1.5; use [Dvariant](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_conditions_pg.html)
172    #[serde(rename = "Dvariant")]
173    pub d_variant: Option<String>,
174}
175
176#[cfg(test)]
177mod tests {
178    use crate::conditions::Conditions;
179
180    #[test]
181    fn parse_conditions() {
182        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
183<conditions>
184    <condition id="CM4">
185        <description>Requires ARM Cortex-M4 with FPU</description>
186        <accept Dcore="Cortex-M4" Dfpu="SP_FPU"/>
187        <accept Dcore="Cortex-M4" Dfpu="DP_FPU"/>
188        <require Tcompiler="GCC"/>
189        <deny Dcore="Cortex-M0"/>
190    </condition>
191    <condition id="GCC_Only">
192        <require Tcompiler="GCC"/>
193    </condition>
194</conditions>"#;
195
196        let conds: Conditions = serde_roxmltree::from_str(xml_str).unwrap();
197        assert_eq!(conds.conditions.len(), 2);
198
199        let c0 = &conds.conditions[0];
200        assert_eq!(c0.id, "CM4");
201        assert_eq!(
202            c0.description,
203            Some("Requires ARM Cortex-M4 with FPU".to_string())
204        );
205        assert_eq!(c0.accept.len(), 2);
206        assert_eq!(c0.accept[0].device_core, Some("Cortex-M4".to_string()));
207        assert_eq!(c0.accept[0].fpu, Some("SP_FPU".to_string()));
208        assert_eq!(c0.accept[1].device_core, Some("Cortex-M4".to_string()));
209        assert_eq!(c0.accept[1].fpu, Some("DP_FPU".to_string()));
210        assert_eq!(c0.require.len(), 1);
211        assert_eq!(c0.require[0].compiler, Some("GCC".to_string()));
212        assert_eq!(c0.deny.len(), 1);
213        assert_eq!(c0.deny[0].device_core, Some("Cortex-M0".to_string()));
214
215        let c1 = &conds.conditions[1];
216        assert_eq!(c1.id, "GCC_Only");
217        assert_eq!(c1.description, None);
218        assert_eq!(c1.accept.len(), 0);
219        assert_eq!(c1.require.len(), 1);
220        assert_eq!(c1.deny.len(), 0);
221    }
222
223    #[test]
224    fn parse_condition_minimal() {
225        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
226<conditions>
227    <condition id="Empty"/>
228</conditions>"#;
229
230        let conds: Conditions = serde_roxmltree::from_str(xml_str).unwrap();
231        assert_eq!(conds.conditions.len(), 1);
232
233        let c = &conds.conditions[0];
234        assert_eq!(c.id, "Empty");
235        assert_eq!(c.description, None);
236        assert_eq!(c.accept, vec![]);
237        assert_eq!(c.require, vec![]);
238        assert_eq!(c.deny, vec![]);
239    }
240
241    #[test]
242    fn parse_condition_filters() {
243        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
244<conditions>
245    <condition id="Complex">
246        <accept Dvendor="ARM:82" Dname="ARMCM4*" Dcore="Cortex-M4"
247                Dfpu="SP_FPU" Dmpu="MPU" Dtz="TZ" Dsecure="Secure"
248                Ddsp="DSP" Dmve="MVE" Dpacbti="PACBTI"
249                Dendian="Little-endian" Pname="Core0"/>
250        <require Cclass="CMSIS" Cgroup="RTOS2" Capiversion="2.0.0"
251                 Cvendor="ARM" Cbundle="CMSIS" Csub="Core"
252                 Cvariant="Release" Cversion="5.0.0"/>
253        <require Bvendor="ARM" Bname="V2M-MPS2" Brevision="Rev.C"
254                 Hvendor="NXP" Hname="LPC1768"
255                 Tcompiler="GCC" Toptions="AC6" Toutput="exe"/>
256        <deny condition="NoARM"/>
257    </condition>
258</conditions>"#;
259
260        let conds: Conditions = serde_roxmltree::from_str(xml_str).unwrap();
261        let c = &conds.conditions[0];
262        assert_eq!(c.id, "Complex");
263
264        // Accept filter — device attributes
265        let a = &c.accept[0];
266        assert_eq!(a.device_vendor, Some("ARM:82".to_string()));
267        assert_eq!(a.device_name, Some("ARMCM4*".to_string()));
268        assert_eq!(a.device_core, Some("Cortex-M4".to_string()));
269        assert_eq!(a.fpu, Some("SP_FPU".to_string()));
270        assert_eq!(a.mpu, Some("MPU".to_string()));
271        assert_eq!(a.trustzone, Some("TZ".to_string()));
272        assert_eq!(a.secure, Some("Secure".to_string()));
273        assert_eq!(a.dsp, Some("DSP".to_string()));
274        assert_eq!(a.mve, Some("MVE".to_string()));
275        assert_eq!(a.pacbti, Some("PACBTI".to_string()));
276        assert_eq!(a.endian, Some("Little-endian".to_string()));
277        assert_eq!(a.processor_name, Some("Core0".to_string()));
278        assert_eq!(a.condition, None);
279
280        // First require filter — component attributes
281        let r0 = &c.require[0];
282        assert_eq!(r0.class, Some("CMSIS".to_string()));
283        assert_eq!(r0.group, Some("RTOS2".to_string()));
284        assert_eq!(r0.api_version, Some("2.0.0".to_string()));
285        assert_eq!(r0.component_vendor, Some("ARM".to_string()));
286        assert_eq!(r0.bundle, Some("CMSIS".to_string()));
287        assert_eq!(r0.sub, Some("Core".to_string()));
288        assert_eq!(r0.variant, Some("Release".to_string()));
289        assert_eq!(r0.version, Some("5.0.0".to_string()));
290        assert_eq!(r0.device_vendor, None);
291
292        // Second require filter — board/part/toolchain attributes
293        let r1 = &c.require[1];
294        assert_eq!(r1.board_vendor, Some("ARM".to_string()));
295        assert_eq!(r1.board_name, Some("V2M-MPS2".to_string()));
296        assert_eq!(r1.board_revision, Some("Rev.C".to_string()));
297        assert_eq!(r1.part_vendor, Some("NXP".to_string()));
298        assert_eq!(r1.part_name, Some("LPC1768".to_string()));
299        assert_eq!(r1.compiler, Some("GCC".to_string()));
300        assert_eq!(r1.compiler_options, Some("AC6".to_string()));
301        assert_eq!(r1.compiler_output, Some("exe".to_string()));
302        assert_eq!(r1.class, None);
303
304        // Deny filter — condition cross-reference
305        let d = &c.deny[0];
306        assert_eq!(d.condition, Some("NoARM".to_string()));
307        assert_eq!(d.device_vendor, None);
308    }
309
310    #[test]
311    fn parse_deprecated_filter_attrs() {
312        // Verifies that deprecated Dfamily / DsubFamily / Dvariant attributes
313        // are captured rather than silently dropped.
314        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
315<conditions>
316    <condition id="LegacyDeviceFilter">
317        <require Dfamily="STM32F4" DsubFamily="STM32F405" Dvariant="STM32F405RGTx"/>
318        <accept Dfamily="NXP_LPC" DsubFamily="LPC176x" Dvariant="LPC1769"/>
319    </condition>
320</conditions>"#;
321
322        let conds: Conditions = serde_roxmltree::from_str(xml_str).unwrap();
323        assert_eq!(conds.conditions.len(), 1);
324
325        let c = &conds.conditions[0];
326        assert_eq!(c.id, "LegacyDeviceFilter");
327
328        let r = &c.require[0];
329        assert_eq!(r.d_family, Some("STM32F4".to_string()));
330        assert_eq!(r.d_sub_family, Some("STM32F405".to_string()));
331        assert_eq!(r.d_variant, Some("STM32F405RGTx".to_string()));
332        // Non-deprecated attrs still absent
333        assert_eq!(r.device_vendor, None);
334        assert_eq!(r.device_name, None);
335
336        let a = &c.accept[0];
337        assert_eq!(a.d_family, Some("NXP_LPC".to_string()));
338        assert_eq!(a.d_sub_family, Some("LPC176x".to_string()));
339        assert_eq!(a.d_variant, Some("LPC1769".to_string()));
340    }
341}