1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
6pub struct Components {
10 pub generator: Option<String>,
12
13 #[serde(rename = "bundle", default)]
15 pub bundles: Vec<Bundle>,
16
17 #[serde(rename = "component", default)]
19 pub components: Vec<Component>,
20}
21
22#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
23pub struct Bundle {
28 #[serde(rename = "Cbundle")]
30 pub bundle: String,
31
32 #[serde(rename = "Cvendor")]
34 pub vendor: Option<String>,
35
36 #[serde(rename = "Cclass")]
38 pub class: String,
39
40 #[serde(rename = "Cversion")]
42 pub version: String,
43
44 #[serde(rename = "licenseSet")]
46 pub license_set: Option<String>,
47
48 pub changelog: Option<String>,
50
51 pub generator: Option<String>,
53
54 #[serde(rename = "isDefaultVariant")]
56 pub is_default_variant: Option<bool>,
57
58 pub description: String,
60
61 pub doc: String,
63
64 #[serde(rename = "component", default)]
66 pub components: Vec<Component>,
67}
68
69#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
70pub struct Component {
75 #[serde(rename = "Cvendor")]
77 pub vendor: Option<String>,
78
79 #[serde(rename = "Cclass")]
81 pub class: Option<String>,
82
83 #[serde(rename = "Cgroup")]
85 pub group: String,
86
87 #[serde(rename = "Csub")]
89 pub sub: Option<String>,
90
91 #[serde(rename = "Cvariant")]
93 pub variant: Option<String>,
94
95 #[serde(rename = "Cversion")]
97 pub version: Option<String>,
98
99 #[serde(rename = "Capiversion")]
101 pub api_version: Option<String>,
102
103 pub condition: Option<String>,
105
106 pub custom: Option<bool>,
108
109 #[serde(rename = "maxInstances")]
111 pub max_instances: Option<u32>,
112
113 #[serde(rename = "isDefaultVariant")]
115 pub is_default_variant: Option<bool>,
116
117 pub generator: Option<String>,
119
120 #[serde(rename = "licenseSet")]
122 pub license_set: Option<String>,
123
124 pub view: Option<String>,
126
127 pub changelog: Option<String>,
129
130 pub deprecated: Option<bool>,
132
133 pub description: String,
135
136 #[serde(rename = "RTE_Components_h")]
138 pub rte_components_h: Option<String>,
139
140 #[serde(rename = "Pre_Include_Global_h")]
142 pub pre_include_global_h: Option<String>,
143
144 #[serde(rename = "Pre_Include_Local_Component_h")]
146 pub pre_include_local_component_h: Option<String>,
147
148 #[serde(default)]
150 pub files: ComponentFiles,
151
152 #[serde(default)]
154 pub extensions: ComponentExtensions,
155
156 pub environments: Option<ComponentEnvironments>,
158}
159
160#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
161pub struct ComponentFiles {
163 #[serde(rename = "file", default)]
165 pub files: Vec<ComponentFile>,
166}
167
168#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
169pub struct ComponentFile {
173 pub name: String,
175
176 pub category: String,
178
179 pub attr: Option<String>,
181
182 pub condition: Option<String>,
184
185 pub version: Option<String>,
187
188 pub select: Option<String>,
190
191 pub src: Option<String>,
193
194 pub path: Option<String>,
196
197 pub language: Option<String>,
199
200 pub scope: Option<String>,
202
203 pub public: Option<bool>,
205
206 pub projectpath: Option<String>,
208
209 pub copy: Option<String>,
211}
212
213#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
214pub struct ComponentExtensions {
216 #[serde(rename = "extension", default)]
218 pub extensions: Vec<ComponentExtension>,
219}
220
221#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
222pub struct ComponentExtension {
226 pub key: String,
228
229 pub value: Option<String>,
231}
232
233#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
234pub struct ComponentEnvironments {
236 #[serde(rename = "environment", default)]
238 pub environments: Vec<ComponentEnvironment>,
239}
240
241#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
242pub struct ComponentEnvironment {
247 pub name: String,
249
250 #[serde(rename = "Pname")]
252 pub processor_name: Option<String>,
253}
254
255#[cfg(test)]
256mod tests {
257 use crate::components::{ComponentEnvironment, ComponentExtension, ComponentFile, Components};
258
259 #[test]
260 fn parse_components() {
261 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
262<components generator="MyGen">
263 <component Cclass="Device" Cgroup="Startup" Cversion="1.0.0"
264 Cvendor="ARM" condition="CM4" licenseSet="all" changelog="CHANGES.txt"
265 custom="false" maxInstances="2" isDefaultVariant="true"
266 generator="StartupGen" view="always">
267 <description>Device startup files</description>
268 <files>
269 <file category="sourceC" name="Device/Source/startup.c"/>
270 <file category="header" name="Device/Include/system.h" public="true"/>
271 </files>
272 <extensions>
273 <extension key="schemaVersion" value="1.0"/>
274 </extensions>
275 </component>
276 <bundle Cbundle="MyRTOS" Cclass="RTOS" Cversion="5.6.0"
277 Cvendor="ARM" licenseSet="rtosLicense" changelog="RTOS_CHANGES.txt">
278 <description>ARM MyRTOS bundle</description>
279 <doc>documentation/MyRTOS.html</doc>
280 <component Cgroup="Kernel" Csub="Source">
281 <description>RTOS kernel source</description>
282 <files>
283 <file category="sourceC" name="RTOS/Source/kernel.c"/>
284 </files>
285 <extensions/>
286 </component>
287 </bundle>
288</components>"#;
289
290 let cs: Components = serde_roxmltree::from_str(xml_str).unwrap();
291 assert_eq!(cs.generator, Some("MyGen".to_string()));
292 assert_eq!(cs.components.len(), 1);
293 assert_eq!(cs.bundles.len(), 1);
294
295 let c = &cs.components[0];
296 assert_eq!(c.vendor, Some("ARM".to_string()));
297 assert_eq!(c.class, Some("Device".to_string()));
298 assert_eq!(c.group, "Startup");
299 assert_eq!(c.version, Some("1.0.0".to_string()));
300 assert_eq!(c.condition, Some("CM4".to_string()));
301 assert_eq!(c.license_set, Some("all".to_string()));
302 assert_eq!(c.changelog, Some("CHANGES.txt".to_string()));
303 assert_eq!(c.custom, Some(false));
304 assert_eq!(c.max_instances, Some(2));
305 assert_eq!(c.is_default_variant, Some(true));
306 assert_eq!(c.generator, Some("StartupGen".to_string()));
307 assert_eq!(c.view, Some("always".to_string()));
308 assert_eq!(c.description, "Device startup files");
309 assert_eq!(c.deprecated, None);
310 assert_eq!(c.sub, None);
311 assert_eq!(c.variant, None);
312 assert_eq!(c.api_version, None);
313 assert_eq!(c.rte_components_h, None);
314 assert_eq!(c.files.files.len(), 2);
315 assert_eq!(c.extensions.extensions.len(), 1);
316 assert_eq!(
317 c.extensions.extensions[0],
318 ComponentExtension {
319 key: "schemaVersion".to_string(),
320 value: Some("1.0".to_string()),
321 }
322 );
323 assert_eq!(c.environments, None);
324
325 let b = &cs.bundles[0];
326 assert_eq!(b.bundle, "MyRTOS");
327 assert_eq!(b.class, "RTOS");
328 assert_eq!(b.version, "5.6.0");
329 assert_eq!(b.vendor, Some("ARM".to_string()));
330 assert_eq!(b.license_set, Some("rtosLicense".to_string()));
331 assert_eq!(b.changelog, Some("RTOS_CHANGES.txt".to_string()));
332 assert_eq!(b.description, "ARM MyRTOS bundle");
333 assert_eq!(b.doc, "documentation/MyRTOS.html");
334 assert_eq!(b.components.len(), 1);
335
336 let bc = &b.components[0];
337 assert_eq!(bc.class, None);
338 assert_eq!(bc.version, None);
339 assert_eq!(bc.group, "Kernel");
340 assert_eq!(bc.sub, Some("Source".to_string()));
341 assert_eq!(bc.description, "RTOS kernel source");
342 }
343
344 #[test]
345 fn parse_component_bundle() {
346 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
347<components>
348 <bundle Cbundle="CMSIS" Cclass="CMSIS" Cversion="5.9.0">
349 <description>CMSIS software framework</description>
350 <doc>CMSIS/Documentation/html/index.html</doc>
351 <component Cgroup="CORE">
352 <description>CMSIS-CORE support for Cortex-M</description>
353 <files>
354 <file category="header" name="CMSIS/Core/Include/cmsis_compiler.h"/>
355 <file category="header" name="CMSIS/Core/Include/core_cm4.h" condition="CM4"/>
356 </files>
357 <extensions/>
358 </component>
359 <component Cgroup="DSP" Cvariant="Source" Cversion="1.10.1"
360 isDefaultVariant="true">
361 <description>CMSIS-DSP library source</description>
362 <files>
363 <file category="sourceC" name="CMSIS/DSP/Source/BasicMathFunctions/arm_abs_f32.c"/>
364 </files>
365 <extensions>
366 <extension key="dsplicense" value="Apache-2.0"/>
367 </extensions>
368 </component>
369 </bundle>
370</components>"#;
371
372 let cs: Components = serde_roxmltree::from_str(xml_str).unwrap();
373 assert_eq!(cs.generator, None);
374 assert_eq!(cs.components.len(), 0);
375 assert_eq!(cs.bundles.len(), 1);
376
377 let b = &cs.bundles[0];
378 assert_eq!(b.bundle, "CMSIS");
379 assert_eq!(b.class, "CMSIS");
380 assert_eq!(b.version, "5.9.0");
381 assert_eq!(b.vendor, None);
382 assert_eq!(b.license_set, None);
383 assert_eq!(b.changelog, None);
384 assert_eq!(b.description, "CMSIS software framework");
385 assert_eq!(b.doc, "CMSIS/Documentation/html/index.html");
386 assert_eq!(b.components.len(), 2);
387
388 let c0 = &b.components[0];
389 assert_eq!(c0.group, "CORE");
390 assert_eq!(c0.class, None);
391 assert_eq!(c0.version, None);
392 assert_eq!(c0.variant, None);
393 assert_eq!(c0.is_default_variant, None);
394 assert_eq!(c0.files.files.len(), 2);
395 assert_eq!(c0.files.files[1].condition, Some("CM4".to_string()));
396 assert_eq!(c0.extensions.extensions.len(), 0);
397
398 let c1 = &b.components[1];
399 assert_eq!(c1.group, "DSP");
400 assert_eq!(c1.variant, Some("Source".to_string()));
401 assert_eq!(c1.version, Some("1.10.1".to_string()));
402 assert_eq!(c1.is_default_variant, Some(true));
403 assert_eq!(c1.extensions.extensions[0].key, "dsplicense");
404 assert_eq!(
405 c1.extensions.extensions[0].value,
406 Some("Apache-2.0".to_string())
407 );
408 }
409
410 #[test]
411 fn parse_component_files() {
412 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
413<components>
414 <component Cclass="USB" Cgroup="Core" Cvariant="Device" Cversion="6.15.0">
415 <deprecated>true</deprecated>
416 <description>USB Device stack (deprecated; use MDK-Middleware instead)</description>
417 <RTE_Components_h>#define RTE_USB_CORE</RTE_Components_h>
418 <Pre_Include_Global_h>#include "usb_config.h"</Pre_Include_Global_h>
419 <files>
420 <file category="header" name="USB/Include/rl_usb.h" scope="public"/>
421 <file category="header" name="USB/Config/USB_Config.h"
422 attr="config" version="6.15.0" select="USB Config"/>
423 <file category="sourceC" name="USB/Source/usbd_core.c"
424 condition="USB_Cond" src="USB/Source/usbd_core.c"
425 language="c" public="false" projectpath="USB/Source"/>
426 <file category="doc" name="https://www.keil.com/pack/doc/mw/USB/html/index.html"/>
427 </files>
428 <extensions>
429 <extension key="schemaVersion" value="2.0"/>
430 <extension key="category" value="middleware"/>
431 </extensions>
432 <environments>
433 <environment name="uv" Pname="Core0"/>
434 </environments>
435 </component>
436</components>"#;
437
438 let cs: Components = serde_roxmltree::from_str(xml_str).unwrap();
439 let c = &cs.components[0];
440
441 assert_eq!(c.class, Some("USB".to_string()));
442 assert_eq!(c.group, "Core");
443 assert_eq!(c.variant, Some("Device".to_string()));
444 assert_eq!(c.version, Some("6.15.0".to_string()));
445 assert_eq!(c.deprecated, Some(true));
446 assert_eq!(
447 c.description,
448 "USB Device stack (deprecated; use MDK-Middleware instead)"
449 );
450 assert_eq!(c.rte_components_h, Some("#define RTE_USB_CORE".to_string()));
451 assert_eq!(
452 c.pre_include_global_h,
453 Some("#include \"usb_config.h\"".to_string())
454 );
455 assert_eq!(c.pre_include_local_component_h, None);
456
457 let files = &c.files.files;
458 assert_eq!(files.len(), 4);
459 assert_eq!(
460 files[0],
461 ComponentFile {
462 name: "USB/Include/rl_usb.h".to_string(),
463 category: "header".to_string(),
464 attr: None,
465 condition: None,
466 version: None,
467 select: None,
468 src: None,
469 path: None,
470 language: None,
471 scope: Some("public".to_string()),
472 public: None,
473 projectpath: None,
474 copy: None,
475 }
476 );
477 assert_eq!(files[1].attr, Some("config".to_string()));
478 assert_eq!(files[1].version, Some("6.15.0".to_string()));
479 assert_eq!(files[1].select, Some("USB Config".to_string()));
480 assert_eq!(files[2].condition, Some("USB_Cond".to_string()));
481 assert_eq!(files[2].src, Some("USB/Source/usbd_core.c".to_string()));
482 assert_eq!(files[2].language, Some("c".to_string()));
483 assert_eq!(files[2].public, Some(false));
484 assert_eq!(files[2].projectpath, Some("USB/Source".to_string()));
485 assert_eq!(
486 files[3].name,
487 "https://www.keil.com/pack/doc/mw/USB/html/index.html"
488 );
489 assert_eq!(files[3].category, "doc");
490
491 let exts = &c.extensions.extensions;
492 assert_eq!(exts.len(), 2);
493 assert_eq!(
494 exts[0],
495 ComponentExtension {
496 key: "schemaVersion".to_string(),
497 value: Some("2.0".to_string())
498 }
499 );
500 assert_eq!(
501 exts[1],
502 ComponentExtension {
503 key: "category".to_string(),
504 value: Some("middleware".to_string())
505 }
506 );
507
508 let envs = c.environments.as_ref().unwrap();
509 assert_eq!(
510 envs.environments,
511 vec![ComponentEnvironment {
512 name: "uv".to_string(),
513 processor_name: Some("Core0".to_string()),
514 }]
515 );
516 }
517}