Skip to main content

cmsis_pdsc_parser/
parts.rs

1//! Contains the types required to represent a [PDSC Parts](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_parts_pg.html#element_parts) element
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
6/// Represents the [PDSC parts](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_parts_pg.html#element_parts) element
7pub struct Parts {
8    /// The list of hardware part descriptions
9    #[serde(rename = "part")]
10    pub parts: Vec<Part>,
11}
12
13#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
14/// Represents a [PDSC part](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_parts_pg.html#element_part) element
15pub struct Part {
16    /// Hardware part vendor name
17    #[serde(rename = "Hvendor")]
18    pub vendor: Option<String>,
19
20    /// Hardware part name
21    #[serde(rename = "Hname")]
22    pub name: String,
23
24    /// Hardware part class
25    #[serde(rename = "Hclass")]
26    pub class: Option<String>,
27
28    /// Hardware part group
29    #[serde(rename = "Hgroup")]
30    pub group: Option<String>,
31
32    /// Hardware part sub-group
33    #[serde(rename = "Hsub")]
34    pub sub: Option<String>,
35
36    /// Exact commercial part name (variant)
37    #[serde(rename = "Hvariant")]
38    pub variant: Option<String>,
39
40    /// Part revision identifier
41    #[serde(rename = "Hrevision")]
42    pub revision: Option<String>,
43
44    /// Brief part description (max 256 characters)
45    pub description: Option<String>,
46
47    /// Part features and capabilities (0..*)
48    #[serde(rename = "feature", default)]
49    pub features: Vec<Feature>,
50
51    /// Documentation files for this part (0..*)
52    #[serde(rename = "book", default)]
53    pub books: Vec<Book>,
54
55    /// Part images (top/bottom/perspective)
56    pub image: Option<Image>,
57
58    /// IDE-specific tool environments for this part (0..*)
59    #[serde(rename = "environment", default)]
60    pub environments: Vec<PartEnvironment>,
61}
62
63#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
64/// Represents a [PDSC part feature](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_family_pg.html#element_feature) element
65pub struct Feature {
66    /// Processor identifier for multi-core parts
67    #[serde(rename = "Pname")]
68    pub processor_name: Option<String>,
69
70    /// Predefined feature type (e.g. `CAN`, `DMA`, `Timer`, `UART`)
71    #[serde(rename = "type")]
72    pub feature_type: String,
73
74    /// Quantity or primary numeric parameter; meaning depends on `feature_type`
75    pub n: Option<String>,
76
77    /// Secondary numeric parameter; meaning depends on `feature_type`
78    pub m: Option<String>,
79
80    /// Descriptive feature name
81    pub name: Option<String>,
82
83    /// Deprecated; only kept for backwards compatibility
84    pub count: Option<i32>,
85}
86
87#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
88/// Represents a [PDSC part book](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_family_pg.html#element_book) element
89pub struct Book {
90    /// Processor identifier for multi-core parts
91    #[serde(rename = "Pname")]
92    pub processor_name: Option<String>,
93
94    /// Document file path or external URL
95    pub name: String,
96
97    /// Display title for the document
98    pub title: String,
99
100    /// Publishing permission; default `true`
101    pub public: Option<bool>,
102}
103
104#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
105/// Represents a [PDSC part image](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_parts_pg.html#element_part_image) element
106pub struct Image {
107    /// Path to the top-side part image
108    pub top: String,
109
110    /// Path to the bottom-side part image
111    pub bottom: Option<String>,
112
113    /// Path to a perspective-view part image
114    pub perspective: Option<String>,
115
116    /// Publishing permission; default `true`
117    pub public: Option<bool>,
118}
119
120#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
121/// Represents a [part environment](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_parts_pg.html#element_part) entry
122pub struct PartEnvironment {
123    /// IDE environment name (e.g. `uvision`, `iar`, `eclipse`)
124    pub name: String,
125
126    /// Processor name for multi-core parts; limits this environment entry to one core
127    #[serde(rename = "Pname")]
128    pub processor_name: Option<String>,
129}
130
131#[cfg(test)]
132mod tests {
133    use crate::parts::{Book, Feature, Image, PartEnvironment, Parts};
134
135    #[test]
136    fn parse_parts() {
137        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
138<parts>
139    <part Hvendor="Microchip" Hname="ATSAMD21G18A" Hclass="Microcontroller" Hgroup="SAM D21"
140          Hsub="SAMD21G18A" Hvariant="TQFP48" Hrevision="A">
141        <description>SAM D21 ARM Cortex-M0+ microcontroller with 256 KB Flash</description>
142        <feature Pname="Cortex-M0+" type="CoreOther" n="1" name="Cortex-M0+ processor"/>
143        <feature type="UART" n="6"/>
144        <book name="docs/SAM_D21_DS.pdf" title="SAM D21 Datasheet" public="true"/>
145        <image top="images/samd21_top.png" bottom="images/samd21_bottom.png" public="true"/>
146    </part>
147</parts>"#;
148
149        let parts: Parts = serde_roxmltree::from_str(xml_str).unwrap();
150        assert_eq!(parts.parts.len(), 1);
151
152        let part = &parts.parts[0];
153        assert_eq!(part.vendor, Some("Microchip".to_string()));
154        assert_eq!(part.name, "ATSAMD21G18A");
155        assert_eq!(part.class, Some("Microcontroller".to_string()));
156        assert_eq!(part.group, Some("SAM D21".to_string()));
157        assert_eq!(part.sub, Some("SAMD21G18A".to_string()));
158        assert_eq!(part.variant, Some("TQFP48".to_string()));
159        assert_eq!(part.revision, Some("A".to_string()));
160        assert_eq!(
161            part.description,
162            Some("SAM D21 ARM Cortex-M0+ microcontroller with 256 KB Flash".to_string())
163        );
164        assert_eq!(
165            part.features,
166            vec![
167                Feature {
168                    processor_name: Some("Cortex-M0+".to_string()),
169                    feature_type: "CoreOther".to_string(),
170                    n: Some("1".to_string()),
171                    m: None,
172                    name: Some("Cortex-M0+ processor".to_string()),
173                    count: None,
174                },
175                Feature {
176                    processor_name: None,
177                    feature_type: "UART".to_string(),
178                    n: Some("6".to_string()),
179                    m: None,
180                    name: None,
181                    count: None,
182                },
183            ]
184        );
185        assert_eq!(
186            part.books,
187            vec![Book {
188                processor_name: None,
189                name: "docs/SAM_D21_DS.pdf".to_string(),
190                title: "SAM D21 Datasheet".to_string(),
191                public: Some(true),
192            }]
193        );
194        assert_eq!(
195            part.image,
196            Some(Image {
197                top: "images/samd21_top.png".to_string(),
198                bottom: Some("images/samd21_bottom.png".to_string()),
199                perspective: None,
200                public: Some(true),
201            })
202        );
203    }
204
205    #[test]
206    fn parse_part_minimal() {
207        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
208<parts>
209    <part Hname="MY-PART-001"/>
210</parts>"#;
211
212        let parts: Parts = serde_roxmltree::from_str(xml_str).unwrap();
213        assert_eq!(parts.parts.len(), 1);
214
215        let part = &parts.parts[0];
216        assert_eq!(part.name, "MY-PART-001");
217        assert_eq!(part.vendor, None);
218        assert_eq!(part.class, None);
219        assert_eq!(part.group, None);
220        assert_eq!(part.sub, None);
221        assert_eq!(part.variant, None);
222        assert_eq!(part.revision, None);
223        assert_eq!(part.description, None);
224        assert_eq!(part.features, vec![]);
225        assert_eq!(part.books, vec![]);
226        assert_eq!(part.image, None);
227    }
228
229    #[test]
230    fn parse_parts_multiple() {
231        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
232<parts>
233    <part Hvendor="ARM" Hname="CM4-CORE">
234        <feature type="CoreOther" n="1"/>
235    </part>
236    <part Hvendor="ARM" Hname="CM0-CORE" Hrevision="r0p1">
237        <book name="docs/cm0.pdf" title="Cortex-M0 Technical Reference Manual"/>
238    </part>
239</parts>"#;
240
241        let parts: Parts = serde_roxmltree::from_str(xml_str).unwrap();
242        assert_eq!(parts.parts.len(), 2);
243
244        assert_eq!(parts.parts[0].vendor, Some("ARM".to_string()));
245        assert_eq!(parts.parts[0].name, "CM4-CORE");
246        assert_eq!(parts.parts[0].features.len(), 1);
247        assert_eq!(parts.parts[0].books, vec![]);
248
249        assert_eq!(parts.parts[1].vendor, Some("ARM".to_string()));
250        assert_eq!(parts.parts[1].name, "CM0-CORE");
251        assert_eq!(parts.parts[1].revision, Some("r0p1".to_string()));
252        assert_eq!(parts.parts[1].features, vec![]);
253        assert_eq!(parts.parts[1].books.len(), 1);
254        assert_eq!(parts.parts[1].books[0].name, "docs/cm0.pdf");
255        assert_eq!(
256            parts.parts[1].books[0].title,
257            "Cortex-M0 Technical Reference Manual"
258        );
259    }
260
261    #[test]
262    fn parse_part_environments() {
263        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
264<parts>
265    <part Hname="MY-CHIP-001">
266        <environment name="uvision"/>
267        <environment name="iar" Pname="Core0"/>
268    </part>
269</parts>"#;
270
271        let parts: Parts = serde_roxmltree::from_str(xml_str).unwrap();
272        assert_eq!(parts.parts.len(), 1);
273
274        let part = &parts.parts[0];
275        assert_eq!(part.name, "MY-CHIP-001");
276        assert_eq!(part.environments.len(), 2);
277        assert_eq!(
278            part.environments[0],
279            PartEnvironment {
280                name: "uvision".to_string(),
281                processor_name: None,
282            }
283        );
284        assert_eq!(
285            part.environments[1],
286            PartEnvironment {
287                name: "iar".to_string(),
288                processor_name: Some("Core0".to_string()),
289            }
290        );
291    }
292}