1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
6pub struct Examples {
10 #[serde(rename = "example", default)]
12 pub examples: Vec<Example>,
13}
14
15#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
16pub struct Example {
20 pub name: String,
22
23 pub folder: String,
25
26 pub archive: Option<String>,
28
29 pub doc: String,
31
32 pub version: Option<String>,
34
35 pub public: Option<bool>,
37
38 pub description: String,
40
41 #[serde(rename = "board", default)]
43 pub boards: Vec<ExampleBoard>,
44
45 pub project: ExampleProject,
47
48 pub attributes: Option<ExampleAttributes>,
50}
51
52#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
53pub struct ExampleBoard {
57 pub vendor: String,
59
60 pub name: String,
62
63 pub revision: Option<String>,
65
66 #[serde(rename = "Dvendor")]
68 pub device_vendor: Option<String>,
69
70 #[serde(rename = "Dname")]
72 pub device_name: Option<String>,
73}
74
75#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
76pub struct ExampleProject {
80 #[serde(rename = "environment", default)]
82 pub environments: Vec<ExampleEnvironment>,
83}
84
85#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
86pub struct ExampleEnvironment {
91 pub name: String,
93
94 pub load: String,
96
97 pub folder: Option<String>,
99}
100
101#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
102pub struct ExampleAttributes {
106 #[serde(rename = "category", default)]
108 pub categories: Vec<String>,
109
110 #[serde(rename = "component", default)]
112 pub components: Vec<ExampleComponent>,
113
114 #[serde(rename = "keyword", default)]
116 pub keywords: Vec<String>,
117}
118
119#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
120pub struct ExampleComponent {
124 #[serde(rename = "Cclass")]
126 pub class: String,
127
128 #[serde(rename = "Cgroup")]
130 pub group: Option<String>,
131
132 #[serde(rename = "Csub")]
134 pub sub: Option<String>,
135
136 #[serde(rename = "Cversion")]
138 pub version: Option<String>,
139
140 #[serde(rename = "Cvendor")]
142 pub vendor: Option<String>,
143
144 #[serde(rename = "Cbundle")]
146 pub bundle: Option<String>,
147
148 #[serde(rename = "Cvariant")]
150 pub variant: Option<String>,
151
152 #[serde(rename = "Capiversion")]
154 pub api_version: Option<String>,
155
156 pub instances: Option<u32>,
158}
159
160#[cfg(test)]
161mod tests {
162 use crate::examples::{
163 ExampleBoard, ExampleComponent, ExampleEnvironment, ExampleProject, Examples,
164 };
165
166 #[test]
167 fn parse_examples() {
168 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
169<examples>
170 <example name="Blinky" folder="examples/Blinky" doc="examples/Blinky/README.md"
171 archive="Blinky.zip" version="1.2.0" public="true">
172 <description>Blinky LED example for Cortex-M4</description>
173 <board vendor="STMicroelectronics" name="NUCLEO-F401RE" revision="B"
174 Dvendor="STMicroelectronics:13" Dname="STM32F401RETx"/>
175 <project>
176 <environment name="uv" load="Blinky.uvprojx"/>
177 <environment name="csolution" load="Blinky.csolution.yml" folder="csolution"/>
178 </project>
179 <attributes>
180 <category>Getting Started</category>
181 <component Cclass="CMSIS" Cgroup="RTOS2" Cversion="2.0.0" Cvendor="ARM"
182 Cbundle="ARM" Cvariant="Keil" Capiversion="1.0.0" instances="2"/>
183 <keyword>LED</keyword>
184 <keyword>Blinky</keyword>
185 </attributes>
186 </example>
187 <example name="Hello" folder="examples/Hello" doc="examples/Hello/README.md">
188 <description>Hello World via UART</description>
189 <project>
190 <environment name="uv" load="Hello.uvprojx"/>
191 </project>
192 </example>
193</examples>"#;
194
195 let examples: Examples = serde_roxmltree::from_str(xml_str).unwrap();
196 assert_eq!(examples.examples.len(), 2);
197
198 let e0 = &examples.examples[0];
199 assert_eq!(e0.name, "Blinky");
200 assert_eq!(e0.folder, "examples/Blinky");
201 assert_eq!(e0.doc, "examples/Blinky/README.md");
202 assert_eq!(e0.archive, Some("Blinky.zip".to_string()));
203 assert_eq!(e0.version, Some("1.2.0".to_string()));
204 assert_eq!(e0.public, Some(true));
205 assert_eq!(e0.description, "Blinky LED example for Cortex-M4");
206 assert_eq!(
207 e0.boards,
208 vec![ExampleBoard {
209 vendor: "STMicroelectronics".to_string(),
210 name: "NUCLEO-F401RE".to_string(),
211 revision: Some("B".to_string()),
212 device_vendor: Some("STMicroelectronics:13".to_string()),
213 device_name: Some("STM32F401RETx".to_string()),
214 }]
215 );
216 assert_eq!(
217 e0.project,
218 ExampleProject {
219 environments: vec![
220 ExampleEnvironment {
221 name: "uv".to_string(),
222 load: "Blinky.uvprojx".to_string(),
223 folder: None,
224 },
225 ExampleEnvironment {
226 name: "csolution".to_string(),
227 load: "Blinky.csolution.yml".to_string(),
228 folder: Some("csolution".to_string()),
229 },
230 ],
231 }
232 );
233 let attrs = e0.attributes.as_ref().unwrap();
234 assert_eq!(attrs.categories, vec!["Getting Started".to_string()]);
235 assert_eq!(
236 attrs.components,
237 vec![ExampleComponent {
238 class: "CMSIS".to_string(),
239 group: Some("RTOS2".to_string()),
240 sub: None,
241 version: Some("2.0.0".to_string()),
242 vendor: Some("ARM".to_string()),
243 bundle: Some("ARM".to_string()),
244 variant: Some("Keil".to_string()),
245 api_version: Some("1.0.0".to_string()),
246 instances: Some(2),
247 }]
248 );
249 assert_eq!(
250 attrs.keywords,
251 vec!["LED".to_string(), "Blinky".to_string()]
252 );
253
254 let e1 = &examples.examples[1];
255 assert_eq!(e1.name, "Hello");
256 assert_eq!(e1.archive, None);
257 assert_eq!(e1.version, None);
258 assert_eq!(e1.boards, vec![]);
259 assert_eq!(e1.attributes, None);
260 }
261
262 #[test]
263 fn parse_example_minimal() {
264 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
265<examples>
266 <example name="Minimal" folder="examples/Minimal" doc="examples/Minimal/README.md">
267 <description>Minimal example</description>
268 <project>
269 <environment name="uv" load="Minimal.uvprojx"/>
270 </project>
271 </example>
272</examples>"#;
273
274 let examples: Examples = serde_roxmltree::from_str(xml_str).unwrap();
275 assert_eq!(examples.examples.len(), 1);
276
277 let e = &examples.examples[0];
278 assert_eq!(e.name, "Minimal");
279 assert_eq!(e.folder, "examples/Minimal");
280 assert_eq!(e.doc, "examples/Minimal/README.md");
281 assert_eq!(e.archive, None);
282 assert_eq!(e.version, None);
283 assert_eq!(e.public, None);
284 assert_eq!(e.description, "Minimal example");
285 assert_eq!(e.boards, vec![]);
286 assert_eq!(e.project.environments.len(), 1);
287 assert_eq!(e.project.environments[0].name, "uv");
288 assert_eq!(e.project.environments[0].load, "Minimal.uvprojx");
289 assert_eq!(e.project.environments[0].folder, None);
290 assert_eq!(e.attributes, None);
291 }
292
293 #[test]
294 fn parse_example_attributes() {
295 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
296<examples>
297 <example name="Tagged" folder="examples/Tagged" doc="examples/Tagged/README.md">
298 <description>Example with rich attributes</description>
299 <project>
300 <environment name="csolution" load="Tagged.csolution.yml"/>
301 </project>
302 <attributes>
303 <category>Middleware</category>
304 <category>Networking</category>
305 <component Cclass="Network" Cgroup="Core" Csub="IPv4" Cversion="7.15.0" Cvendor="Keil"/>
306 <component Cclass="CMSIS"/>
307 <keyword>TCP/IP</keyword>
308 </attributes>
309 </example>
310</examples>"#;
311
312 let examples: Examples = serde_roxmltree::from_str(xml_str).unwrap();
313 let e = &examples.examples[0];
314 let attrs = e.attributes.as_ref().unwrap();
315
316 assert_eq!(
317 attrs.categories,
318 vec!["Middleware".to_string(), "Networking".to_string()]
319 );
320 assert_eq!(
321 attrs.components,
322 vec![
323 ExampleComponent {
324 class: "Network".to_string(),
325 group: Some("Core".to_string()),
326 sub: Some("IPv4".to_string()),
327 version: Some("7.15.0".to_string()),
328 vendor: Some("Keil".to_string()),
329 bundle: None,
330 variant: None,
331 api_version: None,
332 instances: None,
333 },
334 ExampleComponent {
335 class: "CMSIS".to_string(),
336 group: None,
337 sub: None,
338 version: None,
339 vendor: None,
340 bundle: None,
341 variant: None,
342 api_version: None,
343 instances: None,
344 },
345 ]
346 );
347 assert_eq!(attrs.keywords, vec!["TCP/IP".to_string()]);
348 }
349}