Skip to main content

cmsis_pdsc_parser/
apis.rs

1//! Contains the types required to represent a [PDSC APIs](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_apis_pg.html#element_apis) element
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
6/// Represents the [PDSC apis](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_apis_pg.html#element_apis) element
7///
8/// Groups all API definitions published by a pack. At most one `<apis>` section may exist per package.
9pub struct Apis {
10    /// API definitions (1..*)
11    #[serde(rename = "api", default)]
12    pub apis: Vec<Api>,
13}
14
15#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
16/// Represents a [PDSC api](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_apis_pg.html#element_api) element
17///
18/// Defines a software API identified by a component class, group, and optional version.
19pub struct Api {
20    /// Component class identifier
21    #[serde(rename = "Cclass")]
22    pub class: String,
23
24    /// Component group identifier
25    #[serde(rename = "Cgroup")]
26    pub group: String,
27
28    /// API version; part of the API ID when present
29    #[serde(rename = "Capiversion")]
30    pub api_version: Option<String>,
31
32    /// If `false`, multiple implementations of the API may coexist; default is `true`
33    pub exclusive: Option<bool>,
34
35    /// References a condition ID; this API applies only if the condition is met
36    pub condition: Option<String>,
37
38    /// References a `licenseSet` identifier governing usage rights
39    #[serde(rename = "licenseSet")]
40    pub license_set: Option<String>,
41
42    /// References a changelog ID with the API change history
43    pub changelog: Option<String>,
44
45    /// Brief description of the API (max 256 characters)
46    pub description: Option<String>,
47
48    /// Header and documentation files that define the API interface
49    pub files: Option<ApiFiles>,
50}
51
52#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
53/// Represents the `<files>` grouping element inside a [PDSC api](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_apis_pg.html#element_api)
54pub struct ApiFiles {
55    /// Individual file entries (1..*)
56    #[serde(rename = "file", default)]
57    pub files: Vec<ApiFile>,
58}
59
60#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
61/// Represents a `<file>` entry within the API files group
62///
63/// Attributes follow the [PDSC FileType](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_components_pg.html#element_file) definition.
64pub struct ApiFile {
65    /// File path relative to the pack root
66    pub name: String,
67
68    /// File category (e.g. `header`, `include`, `doc`, `sourceC`)
69    pub category: String,
70
71    /// File attribute (e.g. `config`, `template`)
72    pub attr: Option<String>,
73
74    /// References a condition ID; file is included only if condition is met
75    pub condition: Option<String>,
76
77    /// File version
78    pub version: Option<String>,
79
80    /// Selection string used when multiple template files are offered
81    pub select: Option<String>,
82
83    /// Source file for generated or templated files
84    pub src: Option<String>,
85
86    /// Alternate path for the file
87    pub path: Option<String>,
88
89    /// Programming language associated with the file
90    pub language: Option<String>,
91
92    /// Scope of the file within the project
93    pub scope: Option<String>,
94
95    /// Publishing permission; default `true`
96    pub public: Option<bool>,
97
98    /// Project-relative path override for the file
99    pub projectpath: Option<String>,
100
101    /// Deprecated, use `attr="config"` instead
102    pub copy: Option<String>,
103}
104
105#[cfg(test)]
106mod tests {
107    use crate::apis::{Api, ApiFile, ApiFiles, Apis};
108
109    #[test]
110    fn parse_apis() {
111        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
112<apis>
113    <api Cclass="CMSIS" Cgroup="RTOS2" Capiversion="2.1.3" exclusive="false"
114         condition="ARMCC6" licenseSet="all" changelog="Changelog.txt">
115        <description>CMSIS-RTOS2 API for real-time operating systems</description>
116        <files>
117            <file category="header" name="CMSIS/RTOS2/Include/cmsis_os2.h"/>
118            <file category="doc" name="CMSIS/RTOS2/Doc/index.html" public="true"/>
119        </files>
120    </api>
121    <api Cclass="Device" Cgroup="Startup"/>
122</apis>"#;
123
124        let apis: Apis = serde_roxmltree::from_str(xml_str).unwrap();
125        assert_eq!(apis.apis.len(), 2);
126
127        assert_eq!(
128            apis.apis[0],
129            Api {
130                class: "CMSIS".to_string(),
131                group: "RTOS2".to_string(),
132                api_version: Some("2.1.3".to_string()),
133                exclusive: Some(false),
134                condition: Some("ARMCC6".to_string()),
135                license_set: Some("all".to_string()),
136                changelog: Some("Changelog.txt".to_string()),
137                description: Some("CMSIS-RTOS2 API for real-time operating systems".to_string()),
138                files: Some(ApiFiles {
139                    files: vec![
140                        ApiFile {
141                            name: "CMSIS/RTOS2/Include/cmsis_os2.h".to_string(),
142                            category: "header".to_string(),
143                            attr: None,
144                            condition: None,
145                            version: None,
146                            select: None,
147                            src: None,
148                            path: None,
149                            language: None,
150                            scope: None,
151                            public: None,
152                            projectpath: None,
153                            copy: None,
154                        },
155                        ApiFile {
156                            name: "CMSIS/RTOS2/Doc/index.html".to_string(),
157                            category: "doc".to_string(),
158                            attr: None,
159                            condition: None,
160                            version: None,
161                            select: None,
162                            src: None,
163                            path: None,
164                            language: None,
165                            scope: None,
166                            public: Some(true),
167                            projectpath: None,
168                            copy: None,
169                        },
170                    ],
171                }),
172            }
173        );
174        assert_eq!(
175            apis.apis[1],
176            Api {
177                class: "Device".to_string(),
178                group: "Startup".to_string(),
179                api_version: None,
180                exclusive: None,
181                condition: None,
182                license_set: None,
183                changelog: None,
184                description: None,
185                files: None,
186            }
187        );
188    }
189
190    #[test]
191    fn parse_api_minimal() {
192        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
193<apis>
194    <api Cclass="Security" Cgroup="mbed TLS"/>
195</apis>"#;
196
197        let apis: Apis = serde_roxmltree::from_str(xml_str).unwrap();
198        assert_eq!(apis.apis.len(), 1);
199
200        let api = &apis.apis[0];
201        assert_eq!(api.class, "Security");
202        assert_eq!(api.group, "mbed TLS");
203        assert_eq!(api.api_version, None);
204        assert_eq!(api.exclusive, None);
205        assert_eq!(api.condition, None);
206        assert_eq!(api.license_set, None);
207        assert_eq!(api.changelog, None);
208        assert_eq!(api.description, None);
209        assert_eq!(api.files, None);
210    }
211
212    #[test]
213    fn parse_api_files() {
214        let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
215<apis>
216    <api Cclass="USB" Cgroup="Core">
217        <files>
218            <file category="header" name="USB/Include/usb_core.h"
219                  condition="USB_Cond" version="1.0.0" attr="config"
220                  select="USB Core Header" src="USB/Src/usb_core.c"
221                  public="false"/>
222        </files>
223    </api>
224</apis>"#;
225
226        let apis: Apis = serde_roxmltree::from_str(xml_str).unwrap();
227        let api = &apis.apis[0];
228
229        assert_eq!(api.class, "USB");
230        assert_eq!(api.group, "Core");
231        assert_eq!(api.description, None);
232
233        let files = api.files.as_ref().unwrap();
234        assert_eq!(files.files.len(), 1);
235
236        let file = &files.files[0];
237        assert_eq!(file.name, "USB/Include/usb_core.h");
238        assert_eq!(file.category, "header");
239        assert_eq!(file.condition, Some("USB_Cond".to_string()));
240        assert_eq!(file.version, Some("1.0.0".to_string()));
241        assert_eq!(file.attr, Some("config".to_string()));
242        assert_eq!(file.select, Some("USB Core Header".to_string()));
243        assert_eq!(file.src, Some("USB/Src/usb_core.c".to_string()));
244        assert_eq!(file.public, Some(false));
245        assert_eq!(file.path, None);
246        assert_eq!(file.language, None);
247        assert_eq!(file.scope, None);
248        assert_eq!(file.projectpath, None);
249    }
250}