Skip to main content

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}