use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Components {
pub generator: Option<String>,
#[serde(rename = "bundle", default)]
pub bundles: Vec<Bundle>,
#[serde(rename = "component", default)]
pub components: Vec<Component>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Bundle {
#[serde(rename = "Cbundle")]
pub bundle: String,
#[serde(rename = "Cvendor")]
pub vendor: Option<String>,
#[serde(rename = "Cclass")]
pub class: String,
#[serde(rename = "Cversion")]
pub version: String,
#[serde(rename = "licenseSet")]
pub license_set: Option<String>,
pub changelog: Option<String>,
pub generator: Option<String>,
#[serde(rename = "isDefaultVariant")]
pub is_default_variant: Option<bool>,
pub description: String,
pub doc: String,
#[serde(rename = "component", default)]
pub components: Vec<Component>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Component {
#[serde(rename = "Cvendor")]
pub vendor: Option<String>,
#[serde(rename = "Cclass")]
pub class: Option<String>,
#[serde(rename = "Cgroup")]
pub group: String,
#[serde(rename = "Csub")]
pub sub: Option<String>,
#[serde(rename = "Cvariant")]
pub variant: Option<String>,
#[serde(rename = "Cversion")]
pub version: Option<String>,
#[serde(rename = "Capiversion")]
pub api_version: Option<String>,
pub condition: Option<String>,
pub custom: Option<bool>,
#[serde(rename = "maxInstances")]
pub max_instances: Option<u32>,
#[serde(rename = "isDefaultVariant")]
pub is_default_variant: Option<bool>,
pub generator: Option<String>,
#[serde(rename = "licenseSet")]
pub license_set: Option<String>,
pub view: Option<String>,
pub changelog: Option<String>,
pub deprecated: Option<bool>,
pub description: String,
#[serde(rename = "RTE_Components_h")]
pub rte_components_h: Option<String>,
#[serde(rename = "Pre_Include_Global_h")]
pub pre_include_global_h: Option<String>,
#[serde(rename = "Pre_Include_Local_Component_h")]
pub pre_include_local_component_h: Option<String>,
#[serde(default)]
pub files: ComponentFiles,
#[serde(default)]
pub extensions: ComponentExtensions,
pub environments: Option<ComponentEnvironments>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct ComponentFiles {
#[serde(rename = "file", default)]
pub files: Vec<ComponentFile>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct ComponentFile {
pub name: String,
pub category: String,
pub attr: Option<String>,
pub condition: Option<String>,
pub version: Option<String>,
pub select: Option<String>,
pub src: Option<String>,
pub path: Option<String>,
pub language: Option<String>,
pub scope: Option<String>,
pub public: Option<bool>,
pub projectpath: Option<String>,
pub copy: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct ComponentExtensions {
#[serde(rename = "extension", default)]
pub extensions: Vec<ComponentExtension>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct ComponentExtension {
pub key: String,
pub value: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct ComponentEnvironments {
#[serde(rename = "environment", default)]
pub environments: Vec<ComponentEnvironment>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct ComponentEnvironment {
pub name: String,
#[serde(rename = "Pname")]
pub processor_name: Option<String>,
}
#[cfg(test)]
mod tests {
use crate::components::{ComponentEnvironment, ComponentExtension, ComponentFile, Components};
#[test]
fn parse_components() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<components generator="MyGen">
<component Cclass="Device" Cgroup="Startup" Cversion="1.0.0"
Cvendor="ARM" condition="CM4" licenseSet="all" changelog="CHANGES.txt"
custom="false" maxInstances="2" isDefaultVariant="true"
generator="StartupGen" view="always">
<description>Device startup files</description>
<files>
<file category="sourceC" name="Device/Source/startup.c"/>
<file category="header" name="Device/Include/system.h" public="true"/>
</files>
<extensions>
<extension key="schemaVersion" value="1.0"/>
</extensions>
</component>
<bundle Cbundle="MyRTOS" Cclass="RTOS" Cversion="5.6.0"
Cvendor="ARM" licenseSet="rtosLicense" changelog="RTOS_CHANGES.txt">
<description>ARM MyRTOS bundle</description>
<doc>documentation/MyRTOS.html</doc>
<component Cgroup="Kernel" Csub="Source">
<description>RTOS kernel source</description>
<files>
<file category="sourceC" name="RTOS/Source/kernel.c"/>
</files>
<extensions/>
</component>
</bundle>
</components>"#;
let cs: Components = serde_roxmltree::from_str(xml_str).unwrap();
assert_eq!(cs.generator, Some("MyGen".to_string()));
assert_eq!(cs.components.len(), 1);
assert_eq!(cs.bundles.len(), 1);
let c = &cs.components[0];
assert_eq!(c.vendor, Some("ARM".to_string()));
assert_eq!(c.class, Some("Device".to_string()));
assert_eq!(c.group, "Startup");
assert_eq!(c.version, Some("1.0.0".to_string()));
assert_eq!(c.condition, Some("CM4".to_string()));
assert_eq!(c.license_set, Some("all".to_string()));
assert_eq!(c.changelog, Some("CHANGES.txt".to_string()));
assert_eq!(c.custom, Some(false));
assert_eq!(c.max_instances, Some(2));
assert_eq!(c.is_default_variant, Some(true));
assert_eq!(c.generator, Some("StartupGen".to_string()));
assert_eq!(c.view, Some("always".to_string()));
assert_eq!(c.description, "Device startup files");
assert_eq!(c.deprecated, None);
assert_eq!(c.sub, None);
assert_eq!(c.variant, None);
assert_eq!(c.api_version, None);
assert_eq!(c.rte_components_h, None);
assert_eq!(c.files.files.len(), 2);
assert_eq!(c.extensions.extensions.len(), 1);
assert_eq!(
c.extensions.extensions[0],
ComponentExtension {
key: "schemaVersion".to_string(),
value: Some("1.0".to_string()),
}
);
assert_eq!(c.environments, None);
let b = &cs.bundles[0];
assert_eq!(b.bundle, "MyRTOS");
assert_eq!(b.class, "RTOS");
assert_eq!(b.version, "5.6.0");
assert_eq!(b.vendor, Some("ARM".to_string()));
assert_eq!(b.license_set, Some("rtosLicense".to_string()));
assert_eq!(b.changelog, Some("RTOS_CHANGES.txt".to_string()));
assert_eq!(b.description, "ARM MyRTOS bundle");
assert_eq!(b.doc, "documentation/MyRTOS.html");
assert_eq!(b.components.len(), 1);
let bc = &b.components[0];
assert_eq!(bc.class, None);
assert_eq!(bc.version, None);
assert_eq!(bc.group, "Kernel");
assert_eq!(bc.sub, Some("Source".to_string()));
assert_eq!(bc.description, "RTOS kernel source");
}
#[test]
fn parse_component_bundle() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<components>
<bundle Cbundle="CMSIS" Cclass="CMSIS" Cversion="5.9.0">
<description>CMSIS software framework</description>
<doc>CMSIS/Documentation/html/index.html</doc>
<component Cgroup="CORE">
<description>CMSIS-CORE support for Cortex-M</description>
<files>
<file category="header" name="CMSIS/Core/Include/cmsis_compiler.h"/>
<file category="header" name="CMSIS/Core/Include/core_cm4.h" condition="CM4"/>
</files>
<extensions/>
</component>
<component Cgroup="DSP" Cvariant="Source" Cversion="1.10.1"
isDefaultVariant="true">
<description>CMSIS-DSP library source</description>
<files>
<file category="sourceC" name="CMSIS/DSP/Source/BasicMathFunctions/arm_abs_f32.c"/>
</files>
<extensions>
<extension key="dsplicense" value="Apache-2.0"/>
</extensions>
</component>
</bundle>
</components>"#;
let cs: Components = serde_roxmltree::from_str(xml_str).unwrap();
assert_eq!(cs.generator, None);
assert_eq!(cs.components.len(), 0);
assert_eq!(cs.bundles.len(), 1);
let b = &cs.bundles[0];
assert_eq!(b.bundle, "CMSIS");
assert_eq!(b.class, "CMSIS");
assert_eq!(b.version, "5.9.0");
assert_eq!(b.vendor, None);
assert_eq!(b.license_set, None);
assert_eq!(b.changelog, None);
assert_eq!(b.description, "CMSIS software framework");
assert_eq!(b.doc, "CMSIS/Documentation/html/index.html");
assert_eq!(b.components.len(), 2);
let c0 = &b.components[0];
assert_eq!(c0.group, "CORE");
assert_eq!(c0.class, None);
assert_eq!(c0.version, None);
assert_eq!(c0.variant, None);
assert_eq!(c0.is_default_variant, None);
assert_eq!(c0.files.files.len(), 2);
assert_eq!(c0.files.files[1].condition, Some("CM4".to_string()));
assert_eq!(c0.extensions.extensions.len(), 0);
let c1 = &b.components[1];
assert_eq!(c1.group, "DSP");
assert_eq!(c1.variant, Some("Source".to_string()));
assert_eq!(c1.version, Some("1.10.1".to_string()));
assert_eq!(c1.is_default_variant, Some(true));
assert_eq!(c1.extensions.extensions[0].key, "dsplicense");
assert_eq!(
c1.extensions.extensions[0].value,
Some("Apache-2.0".to_string())
);
}
#[test]
fn parse_component_files() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<components>
<component Cclass="USB" Cgroup="Core" Cvariant="Device" Cversion="6.15.0">
<deprecated>true</deprecated>
<description>USB Device stack (deprecated; use MDK-Middleware instead)</description>
<RTE_Components_h>#define RTE_USB_CORE</RTE_Components_h>
<Pre_Include_Global_h>#include "usb_config.h"</Pre_Include_Global_h>
<files>
<file category="header" name="USB/Include/rl_usb.h" scope="public"/>
<file category="header" name="USB/Config/USB_Config.h"
attr="config" version="6.15.0" select="USB Config"/>
<file category="sourceC" name="USB/Source/usbd_core.c"
condition="USB_Cond" src="USB/Source/usbd_core.c"
language="c" public="false" projectpath="USB/Source"/>
<file category="doc" name="https://www.keil.com/pack/doc/mw/USB/html/index.html"/>
</files>
<extensions>
<extension key="schemaVersion" value="2.0"/>
<extension key="category" value="middleware"/>
</extensions>
<environments>
<environment name="uv" Pname="Core0"/>
</environments>
</component>
</components>"#;
let cs: Components = serde_roxmltree::from_str(xml_str).unwrap();
let c = &cs.components[0];
assert_eq!(c.class, Some("USB".to_string()));
assert_eq!(c.group, "Core");
assert_eq!(c.variant, Some("Device".to_string()));
assert_eq!(c.version, Some("6.15.0".to_string()));
assert_eq!(c.deprecated, Some(true));
assert_eq!(
c.description,
"USB Device stack (deprecated; use MDK-Middleware instead)"
);
assert_eq!(c.rte_components_h, Some("#define RTE_USB_CORE".to_string()));
assert_eq!(
c.pre_include_global_h,
Some("#include \"usb_config.h\"".to_string())
);
assert_eq!(c.pre_include_local_component_h, None);
let files = &c.files.files;
assert_eq!(files.len(), 4);
assert_eq!(
files[0],
ComponentFile {
name: "USB/Include/rl_usb.h".to_string(),
category: "header".to_string(),
attr: None,
condition: None,
version: None,
select: None,
src: None,
path: None,
language: None,
scope: Some("public".to_string()),
public: None,
projectpath: None,
copy: None,
}
);
assert_eq!(files[1].attr, Some("config".to_string()));
assert_eq!(files[1].version, Some("6.15.0".to_string()));
assert_eq!(files[1].select, Some("USB Config".to_string()));
assert_eq!(files[2].condition, Some("USB_Cond".to_string()));
assert_eq!(files[2].src, Some("USB/Source/usbd_core.c".to_string()));
assert_eq!(files[2].language, Some("c".to_string()));
assert_eq!(files[2].public, Some(false));
assert_eq!(files[2].projectpath, Some("USB/Source".to_string()));
assert_eq!(
files[3].name,
"https://www.keil.com/pack/doc/mw/USB/html/index.html"
);
assert_eq!(files[3].category, "doc");
let exts = &c.extensions.extensions;
assert_eq!(exts.len(), 2);
assert_eq!(
exts[0],
ComponentExtension {
key: "schemaVersion".to_string(),
value: Some("2.0".to_string())
}
);
assert_eq!(
exts[1],
ComponentExtension {
key: "category".to_string(),
value: Some("middleware".to_string())
}
);
let envs = c.environments.as_ref().unwrap();
assert_eq!(
envs.environments,
vec![ComponentEnvironment {
name: "uv".to_string(),
processor_name: Some("Core0".to_string()),
}]
);
}
}