cmsis_pdsc_parser/generators.rs
1//! Contains the types required to represent a [PDSC Generators](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html) element
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
6/// Represents the [PDSC generators](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_generators) element
7pub struct Generators {
8 /// The list of generator tool descriptions
9 #[serde(rename = "generator")]
10 pub generators: Vec<Generator>,
11}
12
13#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
14/// Represents a [PDSC generator](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_generator) element
15pub struct Generator {
16 /// Unique identifier for this generator, referenced by components via `Gname`
17 pub id: String,
18
19 /// Silicon vendor associated with the generator tool
20 #[serde(rename = "Gvendor")]
21 pub generator_vendor: Option<String>,
22
23 /// Plain-text name of the generator tool
24 #[serde(rename = "Gtool")]
25 pub generator_tool: Option<String>,
26
27 /// Version of the generator tool
28 #[serde(rename = "Gversion")]
29 pub generator_version: Option<String>,
30
31 /// Brief description of the generator (max 256 characters)
32 pub description: Option<String>,
33
34 /// Device or variant the generator targets
35 pub select: Option<Select>,
36
37 /// Output directory for generated files; supports `$P`, `$S` substitution variables
38 #[serde(rename = "workingDir")]
39 pub working_dir: Option<String>,
40
41 /// Path and filename of the generated GPDSC file produced by the tool
42 pub gpdsc: Option<Gpdsc>,
43
44 /// Native executable invocation configuration(s) (0..5)
45 #[serde(default)]
46 pub exe: Vec<Exe>,
47
48 /// Eclipse plug-in invocation configuration
49 pub eclipse: Option<Eclipse>,
50
51 /// Web service invocation configuration
52 pub web: Option<Web>,
53
54 /// Generated project files that the IDE should add to the project after generation
55 pub project_files: Option<ProjectFiles>,
56
57 /// Generator tool files (executables, libraries, etc.) that ship inside the pack
58 pub files: Option<Files>,
59
60 /// Deprecated; use `exe.command` instead
61 pub command: Option<String>,
62
63 /// Deprecated; use `exe.argument` inside `exe` instead
64 pub arguments: Option<GeneratorArguments>,
65 // TODO: extensions — vendor-specific extension section, requires RawNode handling
66}
67
68#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
69/// Represents the [PDSC select](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_select) element
70///
71/// Specifies the device or device variant this generator targets.
72/// Either `device_name` or `device_variant` must be present.
73pub struct Select {
74 /// Silicon vendor of the targeted device (e.g. `"STMicroelectronics:13"`)
75 #[serde(rename = "Dvendor")]
76 pub device_vendor: String,
77
78 /// Device name or wildcard pattern; required if `device_variant` is absent
79 #[serde(rename = "Dname")]
80 pub device_name: Option<String>,
81
82 /// Device variant; required if `device_name` is absent
83 #[serde(rename = "Dvariant")]
84 pub device_variant: Option<String>,
85
86 /// Processor name for multi-core devices
87 #[serde(rename = "Pname")]
88 pub processor_name: Option<String>,
89}
90
91#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
92/// Represents the [PDSC gpdsc](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_gpdsc) element
93///
94/// Identifies the GPDSC file the generator produces. Supports `$P` substitution.
95pub struct Gpdsc {
96 /// Path and filename of the generated GPDSC file relative to `workingDir`
97 pub name: String,
98}
99
100#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
101/// Represents the [PDSC exe](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_exe) element
102///
103/// Defines native executable invocation of the generator tool. Up to four
104/// platform-specific `<command>` entries may be provided.
105pub struct Exe {
106 /// Target host platform this invocation applies to; one of `all` (default), `win`, `linux`, `mac`, `other`
107 pub host: Option<String>,
108
109 /// Platform-specific command lines used to invoke the generator (1..4)
110 #[serde(rename = "command")]
111 pub commands: Vec<Command>,
112
113 /// Arguments appended to the command line
114 #[serde(rename = "argument")]
115 pub arguments: Vec<Argument>,
116}
117
118#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
119/// Represents the [PDSC eclipse](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_eclipse) element
120///
121/// Defines an Eclipse plug-in invocation of the generator tool.
122pub struct Eclipse {
123 /// Eclipse plug-in identifier (e.g. `"com.vendor.generator"`)
124 pub plugin: String,
125
126 /// Fully-qualified Java class within the plug-in to invoke
127 pub class: String,
128
129 /// Method within the class to call
130 pub method: String,
131
132 /// Arguments passed to the Eclipse plug-in method
133 #[serde(rename = "argument")]
134 pub arguments: Vec<EclipseArgument>,
135}
136
137#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
138/// Represents the [PDSC web](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_web) element
139///
140/// Defines a web service invocation of the generator tool.
141pub struct Web {
142 /// URL of the web service endpoint to invoke
143 pub url: String,
144
145 /// Query parameters or arguments passed to the web service
146 #[serde(rename = "argument", default)]
147 pub arguments: Vec<WebArgument>,
148}
149
150#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
151/// Represents a [PDSC command](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_command) element
152///
153/// A single platform-specific command line that invokes the generator executable.
154pub struct Command {
155 /// Target host platform; one of `all`, `win`, `linux`, `mac`, `other`
156 pub host: Option<String>,
157
158 /// Command line string, including path to the executable; supports substitution variables
159 #[serde(rename = "#content")]
160 pub command: String,
161}
162
163#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
164/// Represents a [PDSC argument](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_argument) element
165///
166/// A single argument passed to an `exe` invocation.
167pub struct Argument {
168 /// Invocation mode; one of `normal` (default) or `dry-run`
169 pub mode: Option<String>,
170
171 /// Target host platform; one of `all` (default), `win`, `linux`, `mac`, `other`
172 pub host: Option<String>,
173
174 /// Command-line switch prefix prepended to the argument value (e.g. `"--project"`)
175 pub switch: Option<String>,
176
177 /// The argument value; supports substitution variables
178 #[serde(rename = "#content")]
179 pub value: String,
180}
181
182#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
183/// Represents a [PDSC argument](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_argument) element
184/// for a `web` invocation
185///
186/// Unlike [`Argument`], the web generator argument has no `host` or `mode`, and
187/// `switch` is required rather than optional.
188pub struct WebArgument {
189 /// Command-line switch prefix prepended to the argument value (e.g. `"--board"`); required
190 pub switch: String,
191
192 /// The argument value; supports substitution variables
193 #[serde(rename = "#content")]
194 pub value: String,
195}
196
197#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
198/// Represents a [PDSC argument](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_argument) element
199/// for an `eclipse` invocation
200///
201/// Unlike [`Argument`], the eclipse generator argument has no attributes at all;
202/// it consists solely of text content.
203pub struct EclipseArgument {
204 /// The argument value; supports substitution variables
205 #[serde(rename = "#content")]
206 pub value: String,
207}
208
209#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
210/// Wrapper for the [PDSC project_files](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_project_files) element
211///
212/// Lists the files produced by the generator that the IDE should include in the project.
213pub struct ProjectFiles {
214 /// Generated files to add to the project after generation completes
215 #[serde(rename = "file", default)]
216 pub files: Vec<ProjectFile>,
217}
218
219#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
220/// Wrapper for the [PDSC files](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_files) element
221///
222/// Lists generator tool files (executables, libraries, scripts) that ship inside the pack.
223pub struct Files {
224 /// Generator tool files included in the pack
225 #[serde(rename = "file", default)]
226 pub files: Vec<File>,
227}
228
229#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
230/// Represents a [PDSC file](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_file) entry in [`Files`]
231///
232/// Attributes follow the narrow PDSC `GeneratorFileType` definition (generator tool files
233/// are under sole control of the generator).
234pub struct File {
235 /// File path relative to the pack base directory; supports substitution variables
236 pub name: String,
237
238 /// File category (e.g. `sourceC`, `sourceAsm`, `header`, `library`, `other`)
239 pub category: String,
240
241 /// Condition identifier that controls when this file is included
242 pub condition: Option<String>,
243
244 /// Version of the file
245 pub version: Option<String>,
246}
247
248#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
249/// Represents a [PDSC file](https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/pdsc_generators_pg.html#element_gen_file) entry in [`ProjectFiles`]
250///
251/// Unlike [`File`], `project_files` entries follow the full shared PDSC `FileType`
252/// definition (the same type used for `<file>` elements under components and APIs).
253pub struct ProjectFile {
254 /// References a condition ID; file included only when condition evaluates true
255 pub condition: Option<String>,
256
257 /// File category (e.g. `header`, `sourceC`, `doc`, `library`)
258 pub category: String,
259
260 /// Target compiler/assembler (`c`, `cpp`, `c-cpp`, `asm`, `link`); inferred from extension if absent
261 pub language: Option<String>,
262
263 /// Header visibility (`public` or `private`); default is `public`
264 pub scope: Option<String>,
265
266 /// Special handling: `config` (copied to project, user-editable) or `template`
267 pub attr: Option<String>,
268
269 /// Description/purpose required when `attr="template"`; groups template options
270 pub select: Option<String>,
271
272 /// File path relative to the pack root; may be a URL for `category="doc"`
273 pub name: String,
274
275 /// For `category="header"`: an incomplete include path for project-relative includes
276 pub path: Option<String>,
277
278 /// Copy file to project folder; deprecated, use `attr="config"` instead
279 pub copy: Option<String>,
280
281 /// File-specific version
282 pub version: Option<String>,
283
284 /// Source path relative to PDSC; semicolon-separated list for libraries
285 pub src: Option<String>,
286
287 /// Publishing permission; default `true`
288 pub public: Option<bool>,
289
290 /// IDE project explorer location override
291 pub projectpath: Option<String>,
292}
293
294#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
295/// Deprecated arguments wrapper; use `exe.argument` instead
296pub struct GeneratorArguments {
297 /// Individual argument strings (0..*)
298 #[serde(rename = "argument", default)]
299 pub argument: Vec<String>,
300}
301
302#[cfg(test)]
303mod tests {
304 use crate::generators::{
305 Argument, Command, Eclipse, EclipseArgument, Exe, File, Files, Generators, Gpdsc,
306 ProjectFile, ProjectFiles, Select, Web, WebArgument,
307 };
308
309 #[test]
310 fn parse_generators() {
311 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
312<generators>
313 <generator id="STCubeMX" Gvendor="STMicroelectronics" Gtool="STM32CubeMX" Gversion="6.0.0">
314 <description>STM32CubeMX code generator</description>
315 <select Dvendor="STMicroelectronics:13" Dname="STM32*" Pname="Cortex-M4"/>
316 <workingDir>$P</workingDir>
317 <gpdsc name="$P/MyProject.gpdsc"/>
318 <exe>
319 <command host="win">$S/CubeMX/cubemx.exe</command>
320 <command host="linux">$S/CubeMX/cubemx</command>
321 <argument switch="--project">$P</argument>
322 </exe>
323 <project_files>
324 <file name="main.c" category="sourceC"/>
325 </project_files>
326 <files>
327 <file name="cubemx.exe" category="other" condition="Win" version="6.0.0"/>
328 </files>
329 </generator>
330</generators>"#;
331
332 let generators: Generators = serde_roxmltree::from_str(xml_str).unwrap();
333
334 assert_eq!(generators.generators.len(), 1);
335
336 let generator = &generators.generators[0];
337 assert_eq!(generator.id, "STCubeMX");
338 assert_eq!(
339 generator.generator_vendor,
340 Some("STMicroelectronics".to_string())
341 );
342 assert_eq!(generator.generator_tool, Some("STM32CubeMX".to_string()));
343 assert_eq!(generator.generator_version, Some("6.0.0".to_string()));
344 assert_eq!(
345 generator.description,
346 Some("STM32CubeMX code generator".to_string())
347 );
348 assert_eq!(generator.working_dir, Some("$P".to_string()));
349 assert_eq!(
350 generator.select,
351 Some(Select {
352 device_vendor: "STMicroelectronics:13".to_string(),
353 device_name: Some("STM32*".to_string()),
354 device_variant: None,
355 processor_name: Some("Cortex-M4".to_string()),
356 })
357 );
358 assert_eq!(
359 generator.gpdsc,
360 Some(Gpdsc {
361 name: "$P/MyProject.gpdsc".to_string()
362 })
363 );
364 assert_eq!(
365 generator.exe,
366 vec![Exe {
367 host: None,
368 commands: vec![
369 Command {
370 host: Some("win".to_string()),
371 command: "$S/CubeMX/cubemx.exe".to_string()
372 },
373 Command {
374 host: Some("linux".to_string()),
375 command: "$S/CubeMX/cubemx".to_string()
376 },
377 ],
378 arguments: vec![Argument {
379 mode: None,
380 host: None,
381 switch: Some("--project".to_string()),
382 value: "$P".to_string()
383 },],
384 }]
385 );
386 assert_eq!(
387 generator.project_files,
388 Some(ProjectFiles {
389 files: vec![ProjectFile {
390 condition: None,
391 category: "sourceC".to_string(),
392 language: None,
393 scope: None,
394 attr: None,
395 select: None,
396 name: "main.c".to_string(),
397 path: None,
398 copy: None,
399 version: None,
400 src: None,
401 public: None,
402 projectpath: None,
403 }],
404 })
405 );
406 assert_eq!(
407 generator.files,
408 Some(Files {
409 files: vec![File {
410 name: "cubemx.exe".to_string(),
411 category: "other".to_string(),
412 condition: Some("Win".to_string()),
413 version: Some("6.0.0".to_string()),
414 }],
415 })
416 );
417 assert_eq!(generator.eclipse, None);
418 assert_eq!(generator.web, None);
419 }
420
421 #[test]
422 fn parse_generator_eclipse() {
423 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
424<generators>
425 <generator id="MyEclipseGen">
426 <eclipse plugin="com.example.generator" class="com.example.Generator" method="generate">
427 <argument>$D</argument>
428 <argument>--dry-run</argument>
429 </eclipse>
430 </generator>
431</generators>"#;
432
433 let generators: Generators = serde_roxmltree::from_str(xml_str).unwrap();
434 let generator = &generators.generators[0];
435
436 assert_eq!(generator.id, "MyEclipseGen");
437 assert_eq!(
438 generator.eclipse,
439 Some(Eclipse {
440 plugin: "com.example.generator".to_string(),
441 class: "com.example.Generator".to_string(),
442 method: "generate".to_string(),
443 arguments: vec![
444 EclipseArgument {
445 value: "$D".to_string()
446 },
447 EclipseArgument {
448 value: "--dry-run".to_string()
449 },
450 ],
451 })
452 );
453 assert!(generator.exe.is_empty());
454 assert_eq!(generator.web, None);
455 }
456
457 #[test]
458 fn parse_generator_web() {
459 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
460<generators>
461 <generator id="MyWebGen">
462 <web url="https://generator.example.com/api">
463 <argument switch="--board">$B</argument>
464 </web>
465 </generator>
466</generators>"#;
467
468 let generators: Generators = serde_roxmltree::from_str(xml_str).unwrap();
469 let generator = &generators.generators[0];
470
471 assert_eq!(generator.id, "MyWebGen");
472 assert_eq!(
473 generator.web,
474 Some(Web {
475 url: "https://generator.example.com/api".to_string(),
476 arguments: vec![WebArgument {
477 switch: "--board".to_string(),
478 value: "$B".to_string()
479 },],
480 })
481 );
482 assert!(generator.exe.is_empty());
483 assert_eq!(generator.eclipse, None);
484 }
485
486 #[test]
487 fn parse_generator_deprecated_command_arguments() {
488 // Tests that the deprecated top-level <command> and <arguments> children
489 // of a <generator> element are captured correctly.
490 let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
491<generators>
492 <generator id="OldStyleGen">
493 <description>Legacy generator using deprecated command/arguments elements</description>
494 <command>$S/tools/oldgen</command>
495 <arguments>
496 <argument>--project</argument>
497 <argument>$P</argument>
498 </arguments>
499 </generator>
500</generators>"#;
501
502 let generators: Generators = serde_roxmltree::from_str(xml_str).unwrap();
503 assert_eq!(generators.generators.len(), 1);
504
505 let generator = &generators.generators[0];
506 assert_eq!(generator.id, "OldStyleGen");
507 assert_eq!(generator.command, Some("$S/tools/oldgen".to_string()));
508 let args = generator
509 .arguments
510 .as_ref()
511 .expect("arguments should be present");
512 assert_eq!(
513 args.argument,
514 vec!["--project".to_string(), "$P".to_string(),]
515 );
516 assert!(generator.exe.is_empty());
517 }
518}