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//! 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}