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