Skip to main content

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}