cmsis_pdsc_parser/lib.rs
1//! # CMSIS PDSC Parser
2//!
3//! This is a Rust crate that aims to provide a convenient abstraction to parse
4//! [CMSIS Pack Description Format](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/packFormat.html) (PDSC)
5//! files.\
6//! This project takes in a PDSC file and parses into a Rust datastructure.
7//!
8//! ## Usage
9//!
10//! Add the dependency with the following command:
11//!
12//! ```shell
13//! cargo add cmsis-pdsc-parser
14//! cargo add roxmltree
15//! ```
16//!
17//! Minimal example:
18//!
19//! ```rust,no_run
20//! const PDSC_PATH: &str = "Microchip.PIC32CM-PL_DFP.pdsc";
21//!
22//! fn main() {
23//! // Read the document content into memory
24//! let pdsc_content: String = std::fs::read_to_string(PDSC_PATH).unwrap();
25//!
26//! // Parse the XML document
27//! let document = roxmltree::Document::parse(&pdsc_content).unwrap();
28//! // Parse the PDSC file as the root `Package` element.
29//! let pdsc = cmsis_pdsc_parser::Package::new(&document);
30//!
31//! println!("{:#?}", pdsc);
32//! }
33//! ```
34
35use serde::{Deserialize, Serialize};
36
37pub mod apis;
38pub mod boards;
39pub mod components;
40pub mod conditions;
41pub mod csolution;
42pub mod debug_access;
43pub mod examples;
44pub mod family;
45pub mod generators;
46pub mod part_taxonomy;
47pub mod parts;
48pub mod pdsc;
49pub mod requirements;
50pub mod taxonomy;
51
52#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
53/// Represents [PDSC Package](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_package_pg.html)
54/// which is the root element of the PDSC file
55#[serde(rename_all = "camelCase")]
56pub struct Package<'a> {
57 /// Name of the software pack
58 pub name: String,
59
60 /// Name of the software pack supplier/vendor
61 pub vendor: String,
62
63 /// PDSC schema version; valid values defined by [PDSC schema versioning](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/packFormat.html)
64 pub schema_version: String,
65
66 /// Restricts pack to a specific core; valid values: [DcoreEnum](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/packFormat.html)
67 #[serde(rename = "Dcore")]
68 pub d_core: Option<String>,
69
70 /// Restricts pack to a specific silicon vendor; valid values: [DeviceVendorEnum](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/packFormat.html)
71 #[serde(rename = "Dvendor")]
72 pub d_vendor: Option<String>,
73
74 /// Restricts pack to a specific device name; wildcards allowed
75 #[serde(rename = "Dname")]
76 pub d_name: Option<String>,
77
78 /// Restricts pack to a specific toolchain; valid values: [CompilerEnumType](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/packFormat.html)
79 #[serde(rename = "Tcompiler")]
80 pub t_compiler: Option<String>,
81
82 /// Brief description of the software pack
83 pub description: pdsc::Description,
84
85 /// Export Control Classification Numbers for the EU and US
86 pub eccn: Option<pdsc::Eccn>,
87
88 /// URL or file URI of the software pack
89 pub url: String,
90
91 /// URL or e-mail for users to get support for the Pack content
92 pub support_contact: Option<String>,
93
94 /// Path to the license document of the Pack
95 pub license: Option<String>,
96
97 /// Listing containing the collection of license fils
98 pub license_sets: Option<pdsc::LicenseSets>,
99
100 /// A pack that has dominate attribute overrules other packs
101 pub dominate: Option<pdsc::Dominate>,
102
103 /// Specifies other CMSIS-Packs, programming languages, and compilers required by pack components
104 pub requirements: Option<requirements::Requirements>,
105
106 // The deprecated `create` element is intentionally not modelled.
107 /// HTTPS URL of a public repository that the pack originates from
108 pub repository: Option<pdsc::Repository>,
109
110 /// Version release history with brief information about a software pack
111 pub releases: pdsc::Releases,
112
113 /// Section describing one or more changelog files
114 pub changelogs: Option<pdsc::Changelogs>,
115
116 /// Keywords that might be used to find a software pack
117 pub keywords: Option<pdsc::Keywords>,
118
119 /// Grouping elements for environments information.
120 pub environments: Option<pdsc::Environments>,
121
122 /// Specifies generator tools that have been used to generate components
123 pub generators: Option<generators::Generators>,
124
125 /// Development boards described in this pack
126 pub boards: Option<boards::Boards>,
127
128 /// Hardware parts described in this pack
129 pub parts: Option<parts::Parts>,
130
131 /// Component class and group taxonomy for this pack
132 pub taxonomy: Option<taxonomy::Taxonomy>,
133
134 /// Hardware part class and group taxonomy for this pack
135 #[serde(rename = "part-taxonomy")]
136 pub part_taxonomy: Option<part_taxonomy::PartTaxonomy>,
137
138 /// Application programming interfaces defined by this pack
139 pub apis: Option<apis::Apis>,
140
141 #[serde(borrow)]
142 /// The device family, the devices, and variants
143 pub devices: Option<pdsc::Devices<'a>>,
144
145 /// Conditions defined for use throughout this pack
146 pub conditions: Option<conditions::Conditions>,
147
148 /// Example projects included in this pack
149 pub examples: Option<examples::Examples>,
150
151 /// Software layers and project templates for csolution-based projects
152 pub csolution: Option<csolution::Csolution>,
153
154 /// Components published by this pack
155 pub components: Option<components::Components>,
156}
157
158impl<'a> Package<'a> {
159 /// Parses a PDSC XML document into a [`Package`].
160 ///
161 /// # Errors
162 ///
163 /// Returns [`Error::SerdeRoxmltree`] if the document cannot be deserialized, or
164 /// [`Error::Family`]/[`Error::Debug`] if sequence or statement parsing fails.
165 pub fn new(document: &'a roxmltree::Document) -> Result<Self, Error> {
166 // Parse the content
167 let mut package: Package = serde_roxmltree::from_doc(document)?;
168
169 // Parse the "wild" string contents into structured data
170 for family in package
171 .devices
172 .iter_mut()
173 .flat_map(|devices| devices.families.iter_mut())
174 {
175 family.debugvars = family::merge_debugvars(&family.debugvars_raw)?;
176 family.debugvars.parse_debugvars();
177 family.sequences = family::merge_sequences(&family.sequences_raw)?;
178 family.sequences.parse_sequences()?;
179 family.flashinfo = family::parse_flashinfo(&family.flashinfo_raw)?;
180 for sf in &mut family.sub_families {
181 sf.debugvars = family::merge_debugvars(&sf.debugvars_raw)?;
182 sf.debugvars.parse_debugvars();
183 sf.sequences = family::merge_sequences(&sf.sequences_raw)?;
184 sf.sequences.parse_sequences()?;
185 sf.flashinfo = family::parse_flashinfo(&sf.flashinfo_raw)?;
186 for device in &mut sf.devices {
187 device.debugvars = family::merge_debugvars(&device.debugvars_raw)?;
188 device.debugvars.parse_debugvars();
189 device.sequences = family::merge_sequences(&device.sequences_raw)?;
190 device.sequences.parse_sequences()?;
191 device.flashinfo = family::parse_flashinfo(&device.flashinfo_raw)?;
192 for variant in &mut device.variants {
193 variant.debugvars = family::merge_debugvars(&variant.debugvars_raw)?;
194 variant.debugvars.parse_debugvars();
195 variant.sequences = family::merge_sequences(&variant.sequences_raw)?;
196 variant.sequences.parse_sequences()?;
197 variant.flashinfo = family::parse_flashinfo(&variant.flashinfo_raw)?;
198 }
199 }
200 }
201 for device in &mut family.devices {
202 device.debugvars = family::merge_debugvars(&device.debugvars_raw)?;
203 device.debugvars.parse_debugvars();
204 device.sequences = family::merge_sequences(&device.sequences_raw)?;
205 device.sequences.parse_sequences()?;
206 device.flashinfo = family::parse_flashinfo(&device.flashinfo_raw)?;
207 for variant in &mut device.variants {
208 variant.debugvars = family::merge_debugvars(&variant.debugvars_raw)?;
209 variant.debugvars.parse_debugvars();
210 variant.sequences = family::merge_sequences(&variant.sequences_raw)?;
211 variant.sequences.parse_sequences()?;
212 variant.flashinfo = family::parse_flashinfo(&variant.flashinfo_raw)?;
213 }
214 }
215 }
216
217 // Return the data
218 Ok(package)
219 }
220}
221
222#[derive(Debug, PartialEq, Eq)]
223/// Errors
224pub enum Error {
225 SerdeRoxmltree(serde_roxmltree::Error),
226 Family(family::FamilyParseError),
227 Debug(debug_access::DebugAccessParseError),
228}
229
230impl std::fmt::Display for Error {
231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232 write!(f, "Unable to parse document, got error: {self:?}")
233 }
234}
235
236impl std::error::Error for Error {}
237
238impl From<serde_roxmltree::Error> for Error {
239 fn from(value: serde_roxmltree::Error) -> Self {
240 Self::SerdeRoxmltree(value)
241 }
242}