Skip to main content

omg_idl_code_gen/
lib.rs

1// Copyright (C) 2025  Bryan Conn
2// Copyright (C) 2019  Frank Rehberger
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0>
6mod ast;
7
8use ast::*;
9use omg_idl_grammar::{IdlParser, Rule};
10use pest::{
11    error::ErrorVariant,
12    iterators::{Pair, Pairs},
13    Parser, RuleType,
14};
15use std::{
16    fs::File,
17    io::{self, Read, Write},
18    path::{Path, PathBuf},
19};
20use thiserror::Error;
21
22#[derive(Debug, Error)]
23pub enum IdlError<R: RuleType> {
24    #[error("Failed to parse IDL files")]
25    ParserError(#[from] pest::error::Error<R>),
26    #[error("Could not find requested idl_file: {0:#?}")]
27    FileNotFound(PathBuf),
28    #[error("Failed to render generated code.")]
29    RenderError(#[from] minijinja::Error),
30    #[error("Failed to write generated code.")]
31    WriteError(#[from] io::Error),
32}
33
34/// All IDL Loader must be capable of reading data into the system
35pub trait IdlLoader {
36    fn load(&self, filename: &Path) -> Result<String, io::Error>;
37}
38
39/// Container for where to find a file and if extra logging occur
40#[derive(Debug, Default)]
41pub struct Configuration {
42    search_path: PathBuf,
43    idl_file: PathBuf,
44    verbose: bool,
45}
46
47impl Configuration {
48    pub fn new(search_path: &Path, idl_file: &Path, verbose: bool) -> Self {
49        Self {
50            search_path: search_path.to_path_buf(),
51            idl_file: idl_file.to_path_buf(),
52            verbose,
53        }
54    }
55}
56
57/// Vec to modules. Lower indexes are 'owners' of higher indexes.
58type Scope = Vec<String>;
59
60/// Container for the configuration & root module
61#[derive(Debug, Clone)]
62struct Context<'i> {
63    config: &'i Configuration,
64    root_module: IdlModule,
65}
66
67impl<'i> Context<'i> {
68    pub fn new(config: &'i Configuration) -> Context<'i> {
69        Context {
70            config,
71            root_module: IdlModule::new(None),
72        }
73    }
74
75    /// Find the desired module under the root_module or create it if it
76    /// does not already exist. The module request is made via the scope.
77    fn lookup_module(&mut self, scope: &Scope) -> &mut IdlModule {
78        // Starting from Root traverse the scope-path
79        let mut current_module = &mut self.root_module;
80
81        for name in scope {
82            let submodule = current_module
83                .modules
84                .entry(name.to_owned())
85                .or_insert(IdlModule::new(Some(name.to_owned())));
86            current_module = submodule;
87        }
88
89        current_module
90    }
91
92    /// Add a new entry to the module for the discovered type
93    fn add_type_dcl(&mut self, scope: &Scope, key: String, type_dcl: IdlTypeDcl) {
94        self.lookup_module(scope)
95            .types
96            .entry(key)
97            .or_insert(type_dcl);
98    }
99
100    /// Add a new entry to the module for the discovered const
101    fn add_const_dcl(&mut self, scope: &Scope, key: String, const_dcl: IdlConstDcl) {
102        self.lookup_module(scope)
103            .constants
104            .entry(key)
105            .or_insert(const_dcl);
106    }
107
108    /// type_spec = { template_type_spec | simple_type_spec }
109    pub fn read_type_spec(
110        &mut self,
111        scope: &Scope,
112        pair: Pair<Rule>,
113    ) -> Result<IdlTypeSpec, pest::error::Error<Rule>> {
114        let rule = pair.as_rule();
115        let pos = pair.as_span().start_pos();
116
117        if self.config.verbose {
118            print!("{:indent$}{:?}", "", rule, indent = 3 * scope.len());
119        }
120
121        let type_spec = match rule {
122            Rule::float => IdlTypeSpec::F32Type,
123            Rule::double => IdlTypeSpec::F64Type,
124            Rule::long_double => IdlTypeSpec::F128Type,
125            Rule::unsigned_short_int => IdlTypeSpec::U16Type,
126            Rule::unsigned_longlong_int => IdlTypeSpec::U64Type,
127            Rule::unsigned_long_int => IdlTypeSpec::U32Type,
128            Rule::signed_short_int => IdlTypeSpec::I16Type,
129            Rule::signed_longlong_int => IdlTypeSpec::I64Type,
130            Rule::signed_long_int => IdlTypeSpec::I32Type,
131            Rule::char_type => IdlTypeSpec::CharType,
132            Rule::wide_char_type => IdlTypeSpec::WideCharType,
133            Rule::boolean_type => IdlTypeSpec::BooleanType,
134            Rule::octet_type => IdlTypeSpec::OctetType,
135            Rule::string_type => match pair.into_inner().next() {
136                None => IdlTypeSpec::StringType(None),
137                Some(next_pair) => {
138                    let pos_int_const = self.read_const_expr(scope, next_pair)?;
139                    IdlTypeSpec::StringType(Some(Box::new(pos_int_const)))
140                }
141            },
142            Rule::wide_string_type => {
143                match pair.into_inner().next() {
144                    None => IdlTypeSpec::WideStringType(None),
145                    Some(next_pair) => {
146                        let pos_int_const = self.read_const_expr(scope, next_pair)?;
147                        IdlTypeSpec::WideStringType(Some(Box::new(pos_int_const)))
148                        // Needs to be a &str
149                    }
150                }
151            }
152            Rule::sequence_type => {
153                let pos = pair.as_span().start_pos();
154                let mut inner = pair.into_inner();
155                match (inner.next(), inner.next()) {
156                    (Some(typ), None) => {
157                        let typ_expr = self.read_type_spec(scope, typ)?;
158                        Ok(IdlTypeSpec::SequenceType(Box::new(typ_expr)))
159                    }
160                    (Some(typ), Some(bound)) => {
161                        let typ_expr = self.read_type_spec(scope, typ)?;
162                        let _bound_expr = self.read_const_expr(scope, bound)?;
163                        Ok(IdlTypeSpec::SequenceType(Box::new(typ_expr)))
164                    }
165                    _ => Err(pest::error::Error::new_from_pos(
166                        ErrorVariant::CustomError {
167                            message:
168                                "Failed to discover required components to establish a sequence"
169                                    .to_string(),
170                        },
171                        pos,
172                    )),
173                }?
174            }
175            //  scoped_name = { "::"? ~ identifier ~ ("::" ~ identifier)* }
176            Rule::scoped_name => {
177                let name = self.read_scoped_name(scope, pair)?;
178                IdlTypeSpec::ScopedName(name)
179            }
180            // go deeper
181            _ => match pair.into_inner().next() {
182                Some(pair) => self.read_type_spec(scope, pair),
183                _ => Err(pest::error::Error::new_from_pos(
184                    ErrorVariant::CustomError {
185                        message: "Failed find a deeper rule pair to follow".to_string(),
186                    },
187                    pos,
188                )),
189            }?,
190        };
191
192        Ok(type_spec)
193    }
194
195    /// declarator = { array_declarator | simple_declarator }
196    /// array_declarator = { identifier ~ fixed_array_size+ }
197    /// simple_declarator = { identifier }
198    pub fn read_struct_member_declarator(
199        &mut self,
200        scope: &Scope,
201        pair: Pair<Rule>,
202        type_spec: &IdlTypeSpec,
203    ) -> Result<IdlStructMember, pest::error::Error<Rule>> {
204        let pos = pair.as_span().start_pos();
205        match pair.into_inner().next() {
206            Some(decl) => {
207                let rule = decl.as_rule();
208                let pos = decl.as_span().start_pos();
209                if self.config.verbose {
210                    print!(
211                        "{:indent$}should be declarator {:?}",
212                        "",
213                        rule,
214                        indent = 3 * scope.len()
215                    );
216                }
217                let mut inner = decl.into_inner();
218                match rule {
219                    // simple_declarator = { identifier }
220                    Rule::simple_declarator => match inner.next() {
221                        Some(pair) => Ok(IdlStructMember {
222                            id: self.read_identifier(scope, pair)?,
223                            type_spec: type_spec.clone(),
224                        }),
225                        _ => Err(pest::error::Error::new_from_pos(
226                            ErrorVariant::CustomError {
227                                message: "Pair did not contain a valid IDL simple declarator"
228                                    .to_string(),
229                            },
230                            pos,
231                        )),
232                    },
233                    // array_declarator = { identifier ~ fixed_array_size+ }
234                    Rule::array_declarator => {
235                        match inner.next() {
236                            Some(pair) => {
237                                let array_sizes: Result<Vec<_>, pest::error::Error<Rule>> = inner
238                                    .map(|pair| {
239                                            match pair.into_inner().next() {
240                                                Some(pair) => {
241                                                    // skip node Rule::fixed_array_size and read const_expr underneath
242                                                    self.read_const_expr(scope, pair)
243                                                },
244                                                _ => {
245                                                    Err(pest::error::Error::new_from_pos(
246                                                            ErrorVariant::CustomError {
247                                                                message: "Pair did not contain a valid 'const expression'".to_string(),
248                                                            }, pos))
249                                                }
250                                            }
251                                    })
252                                    .collect();
253                                let array_type_spec = IdlTypeSpec::ArrayType(
254                                    Box::new(type_spec.clone()),
255                                    array_sizes?,
256                                );
257
258                                Ok(IdlStructMember {
259                                    id: self.read_identifier(scope, pair)?,
260                                    type_spec: array_type_spec,
261                                })
262                            }
263                            _ => Err(pest::error::Error::new_from_pos(
264                                ErrorVariant::CustomError {
265                                    message: "Pair did not contain a valid IDL array declatator"
266                                        .to_string(),
267                                },
268                                pos,
269                            )),
270                        }
271                    }
272                    _ => Err(pest::error::Error::new_from_pos(
273                        ErrorVariant::CustomError {
274                            message: "Pair did not contain a valid IDL type rule".to_string(),
275                        },
276                        pos,
277                    )),
278                }
279            }
280            _ => Err(pest::error::Error::new_from_pos(
281                ErrorVariant::CustomError {
282                    message: "Pair did not contain a either other a simple or arraty declarator"
283                        .to_string(),
284                },
285                pos,
286            )),
287        }
288    }
289
290    // member = { type_spec ~ declarators ~ ";" }
291    // declarators = { declarator ~ ("," ~ declarator )* }
292    // declarator = { array_declarator | simple_declarator }
293    fn read_struct_member(
294        &mut self,
295        scope: &Scope,
296        pair: Pair<Rule>,
297    ) -> Result<Vec<IdlStructMember>, pest::error::Error<Rule>> {
298        let pos = pair.as_span().start_pos();
299
300        if self.config.verbose {
301            print!(
302                "{:indent$}{:?}",
303                "",
304                pair.as_rule(),
305                indent = 3 * scope.len()
306            );
307        }
308
309        let mut inner = pair.into_inner();
310        let type_spec = match inner.next() {
311            Some(pair) => self.read_type_spec(scope, pair),
312            _ => Err(pest::error::Error::new_from_pos(
313                ErrorVariant::CustomError {
314                    message: "Pair did not contain a valid IDL type rule".to_string(),
315                },
316                pos,
317            )),
318        }?;
319
320        // skip rule 'declarators' and parse sibblings `declarator'
321        let declarators = match inner.next() {
322            Some(pair) => Ok(pair.into_inner()),
323            _ => Err(pest::error::Error::new_from_pos(
324                ErrorVariant::CustomError {
325                    message: "Failed to aquire next declaration pair".to_string(),
326                },
327                pos,
328            )),
329        }?;
330
331        declarators
332            .map(|declarator| self.read_struct_member_declarator(scope, declarator, &type_spec))
333            .collect()
334    }
335
336    /// identifier = @{ (alpha | "_") ~ ("_" | alpha | digit)* }
337    fn read_identifier(
338        &mut self,
339        scope: &Scope,
340        pair: Pair<Rule>,
341    ) -> Result<String, pest::error::Error<Rule>> {
342        let rule = pair.as_rule();
343        if self.config.verbose {
344            println!("{:indent$}{:?}", "", rule, indent = 3 * scope.len());
345        }
346        match rule {
347            Rule::identifier | Rule::enumerator => Ok(pair.as_str().to_owned()),
348            _ => Err(pest::error::Error::new_from_pos(
349                ErrorVariant::CustomError {
350                    message: "Pair did not contain a valid scoped name rule".to_string(),
351                },
352                pair.as_span().start_pos(),
353            )),
354        }
355    }
356
357    /// scoped_name = { "::"? ~ identifier ~ ("::" ~ identifier)* }
358    fn read_scoped_name(
359        &mut self,
360        scope: &Scope,
361        pair: Pair<Rule>,
362    ) -> Result<IdlScopedName, pest::error::Error<Rule>> {
363        let is_absolute_name = pair.as_str().starts_with("::");
364        if self.config.verbose {
365            println!(
366                "{:indent$}>>> {:?} '{}' - abs? {is_absolute_name}",
367                "",
368                pair.as_rule(),
369                pair.as_str(),
370                indent = 3 * scope.len()
371            );
372        }
373
374        // check if name starts with "::"
375        let inner = pair.into_inner();
376        let scoped_name: Result<Vec<String>, pest::error::Error<Rule>> = inner
377            .map(|pair| self.read_identifier(scope, pair))
378            .collect();
379
380        Ok(IdlScopedName(scoped_name?, is_absolute_name))
381    }
382
383    /// const_expr = { unary_expr ~ (or_expr | xor_expr | and_expr | shift_expr | add_expr | mult_expr)? }
384    fn read_const_expr(
385        &mut self,
386        scope: &Scope,
387        pair: Pair<Rule>,
388    ) -> Result<IdlValueExpr, pest::error::Error<Rule>> {
389        let rule = pair.as_rule();
390        let pos = pair.as_span().start_pos();
391
392        if self.config.verbose {
393            println!(
394                "{:indent$}{:?} '{}'",
395                "",
396                rule,
397                pair.as_str(),
398                indent = 3 * scope.len()
399            );
400        }
401        let fp_collect_init = (None, None, None, None);
402
403        let fp_collect = |(i, f, e, s), node: Pair<Rule>| match node.as_rule() {
404            Rule::integral_part => (Some(node.as_str().to_owned()), f, e, s),
405            Rule::fractional_part => (i, Some(node.as_str().to_owned()), e, s),
406            Rule::exponent => (i, f, Some(node.as_str().to_owned()), s),
407            Rule::float_suffix => (i, f, e, Some(node.as_str().to_owned())),
408            _ => panic!(),
409        };
410
411        let mut binary_op_collect =
412            |pair: Option<Pair<'_, Rule>>, bin_op: BinaryOp, err_str: &str| match pair {
413                Some(pair) => {
414                    let expr = self.read_const_expr(scope, pair)?;
415                    Ok(IdlValueExpr::BinaryOp(bin_op, Box::new(expr)))
416                }
417                None => Err(pest::error::Error::new_from_pos(
418                    ErrorVariant::CustomError {
419                        message: format!("No associated values found with the parsed '{err_str}'"),
420                    },
421                    pos,
422                )),
423            };
424
425        let pair_as_str = pair.as_str();
426        let mut inner = pair.into_inner();
427        match rule {
428            Rule::const_expr => match (inner.next(), inner.next()) {
429                (Some(expr0), Some(expr1)) => {
430                    let value_expr_0 = self.read_const_expr(scope, expr0)?;
431                    let value_expr_1 = self.read_const_expr(scope, expr1)?;
432                    Ok(IdlValueExpr::Expr(Box::new(value_expr_0), Box::new(value_expr_1)))
433                }
434                (Some(expr1), None) => self.read_const_expr(scope, expr1),
435                _ => Err(pest::error::Error::new_from_pos(
436                    ErrorVariant::CustomError {
437                        message: "Pair did not contain a valid const expression rule".to_string(),
438                    }, pos)),
439            },
440            Rule::unary_expr => match (inner.next(), inner.next()) {
441                (Some(unary_op), Some(prim_expr)) => {
442                    let pos = prim_expr.as_span().start_pos();
443                    let expr = self.read_const_expr(scope, prim_expr)?;
444                    match unary_op.as_str() {
445                        "-" => Ok(IdlValueExpr::UnaryOp(UnaryOp::Neg, Box::new(expr))),
446                        "+" => Ok(IdlValueExpr::UnaryOp(UnaryOp::Pos, Box::new(expr))),
447                        "~" => Ok(IdlValueExpr::UnaryOp(UnaryOp::Inverse, Box::new(expr))),
448                        _ => Err(pest::error::Error::new_from_pos(
449                            ErrorVariant::CustomError {
450                                message: format!("{unary_op} does not match acceptable values -|+|~"),
451                            }, pos)),
452                    }
453                }
454                (Some(prim_expr), None) => {
455                    self.read_const_expr(scope, prim_expr)
456                },
457                _ => Err(pest::error::Error::new_from_pos(
458                    ErrorVariant::CustomError {
459                        message: "Rule does not match expected unary expression format".to_string(),
460                    }, pos)),
461            },
462            Rule::primary_expr => match inner.next() {
463                //  scoped_name = { "::"? ~ identifier ~ ("::" ~ identifier)* }
464                Some(pair) if pair.as_rule() == Rule::scoped_name => {
465                    let name = self.read_scoped_name(scope, pair)?;
466                    Ok(IdlValueExpr::ScopedName(name))
467                }
468                Some(pair) if pair.as_rule() == Rule::literal => {
469                    self.read_const_expr(scope, pair)
470                },
471                Some(pair) if pair.as_rule() == Rule::const_expr => {
472                    let expr = self.read_const_expr(scope, pair)?;
473                    Ok(IdlValueExpr::Brace(Box::new(expr)))
474                }
475                _ => Err(pest::error::Error::new_from_pos(
476                    ErrorVariant::CustomError {
477                        message: "Primary expression did not match 'scoped_name', 'literal', or 'const expression'".to_string(),
478                    }, pos)),
479            },
480            Rule::and_expr => {
481                binary_op_collect(inner.next(), BinaryOp::And, "and expression")
482            }
483            Rule::or_expr => {
484                binary_op_collect(inner.next(), BinaryOp::Or, "or expression")
485            }
486            Rule::xor_expr => {
487                binary_op_collect(inner.next(), BinaryOp::Xor, "xor expression")
488            }
489            Rule::lshift_expr => {
490                binary_op_collect(inner.next(), BinaryOp::LShift, "left shift expression")
491            }
492            Rule::rshift_expr => {
493                binary_op_collect(inner.next(), BinaryOp::RShift, "right shift expression")
494            }
495            Rule::add_expr => {
496                binary_op_collect(inner.next(), BinaryOp::Add, "add expression")
497            }
498            Rule::sub_expr => {
499                binary_op_collect(inner.next(), BinaryOp::Sub, "sub expression")
500            }
501            Rule::mul_expr => {
502                binary_op_collect(inner.next(), BinaryOp::Mul, "multiply expression")
503            }
504            Rule::div_expr => {
505                binary_op_collect(inner.next(), BinaryOp::Div, "division expression")
506            }
507            Rule::mod_expr => {
508                binary_op_collect(inner.next(), BinaryOp::Mod, "modulo expression")
509            }
510            Rule::decimal_integer_literal => Ok(IdlValueExpr::DecLiteral(pair_as_str.to_owned())),
511            Rule::octal_integer_literal => Ok(IdlValueExpr::OctLiteral(pair_as_str.to_owned())),
512            Rule::hex_integer_literal => Ok(IdlValueExpr::HexLiteral(pair_as_str.to_owned())),
513            Rule::floating_pt_literal => {
514                let (i, f, e, s) = inner.fold(fp_collect_init, fp_collect);
515                Ok(IdlValueExpr::FloatLiteral(i, f, e, s))
516            }
517            Rule::boolean_literal => {
518                let true_str = "TRUE".to_string();
519                Ok(IdlValueExpr::BooleanLiteral(pair_as_str.to_uppercase() == true_str))
520            },
521            Rule::character_literal => Ok(IdlValueExpr::CharLiteral(pair_as_str.to_owned())),
522            Rule::wide_character_literal => {
523                Ok(IdlValueExpr::WideCharLiteral(pair_as_str.to_owned()))
524            }
525            Rule::string_literal => Ok(IdlValueExpr::StringLiteral(pair_as_str.to_owned())),
526            Rule::wide_string_literal => {
527                Ok(IdlValueExpr::WideStringLiteral(pair_as_str.to_owned()))
528            }
529            _ => {
530                match inner.next() {
531                    Some(pair) => {
532                        self.read_const_expr(scope, pair)
533                    }
534                    None => {
535                        Err(pest::error::Error::new_from_pos(
536                        ErrorVariant::CustomError {
537                            message: "Failed to read 'const expression' in the catch all".to_string(),
538                        }, pos))
539                    }
540                }
541            },
542        }
543    }
544
545    /// declarator = { array_declarator | simple_declarator }
546    /// array_declarator = { identifier ~ fixed_array_size+ }
547    /// simple_declarator = { identifier }
548    fn read_switch_element_declarator(
549        &mut self,
550        scope: &Scope,
551        pair: Pair<Rule>,
552        type_spec: &IdlTypeSpec,
553    ) -> Result<IdlSwitchElement, pest::error::Error<Rule>> {
554        let pos = pair.as_span().start_pos();
555
556        match pair.into_inner().next() {
557            Some(decl) => {
558                let rule = decl.as_rule();
559                let pos = decl.as_span().start_pos();
560                if self.config.verbose {
561                    println!(
562                        "{:indent$}should be declarator {:?}",
563                        "",
564                        rule,
565                        indent = 3 * scope.len()
566                    );
567                }
568
569                let mut inner = decl.into_inner();
570                match rule {
571                    // simple_declarator = { identifier }
572                    Rule::simple_declarator => {
573                        match inner.next() {
574                            Some(pair) => {
575                                Ok(IdlSwitchElement {
576                                    id: self.read_identifier(scope, pair)?,
577                                    type_spec: type_spec.clone(),
578                                })
579                            }
580                            _ => {
581                                Err(pest::error::Error::<Rule>::new_from_pos(
582                                    ErrorVariant::CustomError {
583                                        message: "Failed to discover simple declarator for switch element".to_string(),
584                                    }, pos))
585                            },
586                        }
587                    }
588                    // array_declarator = { identifier ~ fixed_array_size+ }
589                    Rule::array_declarator => {
590                        match inner.next() {
591                            Some(pair) => {
592                                let id = self.read_identifier(scope, pair)?;
593                                let array_sizes: Result<Vec<_>, pest::error::Error<Rule>> = inner
594                                    .map(|pair| {
595                                            let pos = pair.as_span().start_pos();
596                                            match pair.into_inner().next() {
597                                                Some(pair) => {
598                                                    // skip node Rule::fixed_array_size and read const_expr underneath
599                                                    self.read_const_expr(scope, pair)
600                                                }
601                                                _ => Err(pest::error::Error::new_from_pos(
602                                                        ErrorVariant::CustomError {
603                                                            message: "Failed to discover const_expr under the fixed_array_size for switch element declarator".to_string(),
604                                                        }, pos)),
605                                            }
606                                    })
607                                    .collect();
608                                let array_type_spec =
609                                    IdlTypeSpec::ArrayType(Box::new(type_spec.clone()), array_sizes?);
610
611                                Ok(IdlSwitchElement {
612                                    id,
613                                    type_spec: array_type_spec,
614                                })
615                            }
616                            _ => Err(pest::error::Error::new_from_pos(
617                                    ErrorVariant::CustomError {
618                                        message: "Failed to parse array declarator for switch element declarator".to_string(),
619                                    }, pos)),
620                        }
621                    },
622                    _ => Err(pest::error::Error::new_from_pos(
623                            ErrorVariant::CustomError {
624                                message: "Failed to discover either simple or array declarator for switch element declarator".to_string(),
625                            }, pos)),
626
627                }
628            }
629            _ => Err(pest::error::Error::new_from_pos(
630                ErrorVariant::CustomError {
631                    message: "Failed to parse switch element declarator".to_string(),
632                },
633                pos,
634            )),
635        }
636    }
637
638    /// element_spec = { type_spec ~ declarator }
639    fn read_switch_element_spec(
640        &mut self,
641        scope: &Scope,
642        pair: Pair<Rule>,
643    ) -> Result<IdlSwitchElement, pest::error::Error<Rule>> {
644        let rule = pair.as_rule();
645        let pos = pair.as_span().start_pos();
646        if self.config.verbose {
647            println!("{:indent$}{:?}", "", rule, indent = 3 * scope.len());
648        }
649        let mut inner = pair.into_inner();
650        match inner.next() {
651            Some(pair) => {
652                let type_spec = self.read_type_spec(scope, pair)?;
653                match inner.next() {
654                    Some(pair) => self.read_switch_element_declarator(scope, pair, &type_spec),
655                    _ => Err(pest::error::Error::new_from_pos(
656                        ErrorVariant::CustomError {
657                            message: "Failed to read declarator from the switch element spec"
658                                .to_string(),
659                        },
660                        pos,
661                    )),
662                }
663            }
664            _ => Err(pest::error::Error::new_from_pos(
665                ErrorVariant::CustomError {
666                    message: "Failed to read type spec from the switch element spec".to_string(),
667                },
668                pos,
669            )),
670        }
671    }
672
673    /// switch_type_spec = {integer_type | char_type | boolean_type | wide_char_type | octet_type | scoped_name }
674    fn read_switch_type_spec(
675        &mut self,
676        scope: &Scope,
677        pair: Pair<Rule>,
678    ) -> Result<IdlTypeSpec, pest::error::Error<Rule>> {
679        let rule = pair.as_rule();
680        let pos = pair.as_span().start_pos();
681        if self.config.verbose {
682            println!("{:indent$}{:?}", "", rule, indent = 3 * scope.len());
683        }
684        match pair.into_inner().next() {
685            Some(pair) => self.read_type_spec(scope, pair),
686            _ => Err(pest::error::Error::new_from_pos(
687                ErrorVariant::CustomError {
688                    message: "Failed to read associated type for switch type".to_string(),
689                },
690                pos,
691            )),
692        }
693    }
694
695    /// switch_body = { case+ }
696    fn read_switch_body(
697        &mut self,
698        scope: &Scope,
699        pair: Pair<Rule>,
700    ) -> Result<Vec<IdlSwitchCase>, pest::error::Error<Rule>> {
701        let rule = pair.as_rule();
702        if self.config.verbose {
703            println!("{:indent$}{:?}", "", rule, indent = 3 * scope.len());
704        }
705
706        pair.into_inner()
707            .map(|pair| self.read_switch_case(scope, pair))
708            .collect()
709    }
710
711    /// case_label = { "case" ~ const_expr ~ ":" | "default" ~ ":" }
712    fn read_switch_label(
713        &mut self,
714        scope: &Scope,
715        pair: Pair<Rule>,
716    ) -> Result<IdlSwitchLabel, pest::error::Error<Rule>> {
717        if self.config.verbose {
718            println!(
719                "{:indent$}{:?}",
720                "",
721                pair.as_rule(),
722                indent = 3 * scope.len()
723            );
724        }
725
726        match pair.into_inner().next() {
727            Some(pair) => {
728                let expr = self.read_const_expr(scope, pair)?;
729                Ok(IdlSwitchLabel::Label(expr))
730            }
731            _ => Ok(IdlSwitchLabel::Default),
732        }
733    }
734
735    /// case = { case_label+ ~ element_spec ~ ";" }
736    fn read_switch_case(
737        &mut self,
738        scope: &Scope,
739        pair: Pair<Rule>,
740    ) -> Result<IdlSwitchCase, pest::error::Error<Rule>> {
741        if self.config.verbose {
742            println!(
743                "{:indent$}{:?}",
744                "",
745                pair.as_rule(),
746                indent = 3 * scope.len()
747            );
748        }
749
750        let inner = pair.into_inner();
751        let case_labels: Result<Vec<IdlSwitchLabel>, pest::error::Error<Rule>> = inner
752            .clone()
753            .filter(|p| p.as_rule() == Rule::case_label)
754            .map(|p| self.read_switch_label(scope, p))
755            .collect();
756
757        // there will be only one in the list, choose the last
758        let elem_spec = inner
759            .filter(|p| p.as_rule() == Rule::element_spec)
760            .map(|p| self.read_switch_element_spec(scope, p))
761            .last()
762            .unwrap();
763
764        Ok(IdlSwitchCase {
765            labels: case_labels?,
766            elem_spec: elem_spec?,
767        })
768    }
769
770    /// declarator = { array_declarator | simple_declarator }
771    /// array_declarator = { identifier ~ fixed_array_size+ }
772    /// simple_declarator = { identifier }
773    fn process_declarator(
774        &mut self,
775        scope: &Scope,
776        pair: Pair<Rule>,
777        type_spec: &IdlTypeSpec,
778    ) -> Result<(), pest::error::Error<Rule>> {
779        let pos = pair.as_span().start_pos();
780        match pair.into_inner().next() {
781            Some(decl) => {
782                let rule = decl.as_rule();
783                let pos = decl.as_span().start_pos();
784                if self.config.verbose {
785                    println!("{:indent$}{:?}", "", rule, indent = 3 * scope.len());
786                }
787                let mut inner = decl.clone().into_inner();
788
789                match rule {
790                    Rule::simple_declarator => {
791                        match inner.next() {
792                            Some(pair) => {
793                                let id = self.read_identifier(scope, pair)?;
794                                let type_dcl = IdlTypeDcl(IdlTypeDclKind::TypeDcl(id.clone(), type_spec.clone()));
795                                self.add_type_dcl(scope, id, type_dcl);
796                                Ok(())
797                            },
798                            _ => {
799                                Err(pest::error::Error::new_from_pos(
800                                ErrorVariant::CustomError {
801                                    message: "Pair did not contain a valid identifer for the simple declarator".to_string(),
802                                }, pos))
803                            }
804                        }
805                    },
806                    // array_declarator = { identifier ~ fixed_array_size+ }
807                    Rule::array_declarator => {
808                        match inner.next() {
809                            Some(pair) => {
810                                let id = self.read_identifier(scope, pair)?;
811                                let key = id.clone();
812
813                                let array_sizes: Result<Vec<_>, pest::error::Error<Rule>> = inner
814                                    .map(|pair| {
815                                        match pair.into_inner().next() {
816                                            Some(pair) => {
817                                                // skip node Rule::fixed_array_size and read const_expr underneath
818                                                self.read_const_expr(scope, pair)
819                                            },
820                                            _ => {
821                                                Err(pest::error::Error::new_from_pos(
822                                                ErrorVariant::CustomError {
823                                                    message: "Failed to read the const expr under the Rule::fixed_array_size".to_string(),
824                                                }, pos))
825                                            }
826                                        }
827                                    })
828                                    .collect();
829                                let array_type_spec =
830                                    IdlTypeSpec::ArrayType(Box::new(type_spec.clone()), array_sizes?);
831                                let type_dcl = IdlTypeDcl(IdlTypeDclKind::TypeDcl(id, array_type_spec));
832                                self.add_type_dcl(scope, key, type_dcl);
833                                Ok(())
834                            },
835                            _ => {
836                                Err(pest::error::Error::new_from_pos(
837                                ErrorVariant::CustomError {
838                                    message: "Pair did not contain a valid identifer for the simple declarator".to_string(),
839                                }, pos))
840                            }
841                        }
842                    },
843                    _ => {
844                        Err(pest::error::Error::new_from_pos(
845                        ErrorVariant::CustomError {
846                            message: "Pair did not contain a valid simple or array declarator".to_string(),
847                        }, pos))
848                    }
849                }
850            }
851            // traverse deeper
852            _ => Err(pest::error::Error::new_from_pos(
853                ErrorVariant::CustomError {
854                    message: "No associated values found with the parsed array|simple declarator"
855                        .to_string(),
856                },
857                pos,
858            )),
859        }
860    }
861
862    /// Walk through all discovered pairs and create the associated objs
863    fn process<L: IdlLoader>(
864        &mut self,
865        scope: &mut Scope,
866        loader: &mut dyn IdlLoader,
867        pair: Pair<Rule>,
868    ) -> Result<(), IdlError<Rule>> {
869        let mut iter = pair.clone().into_inner();
870        if self.config.verbose {
871            println!(
872                "{:indent$}{:?}",
873                "",
874                pair.as_rule(),
875                indent = 3 * scope.len()
876            );
877        }
878        match pair.as_rule() {
879            // module_dcl = { "module" ~ identifier ~ "{" ~ definition* ~ "}" }
880            Rule::module_dcl => {
881                let id = iter.next().unwrap().as_str();
882
883                scope.push(id.to_owned());
884
885                let _ = self.lookup_module(scope);
886
887                for p in iter {
888                    let _ = self.process::<L>(scope, loader, p);
889                }
890
891                let _ = scope.pop();
892
893                Ok(())
894            }
895            // struct_def = { "struct" ~ identifier ~ (":" ~ scoped_name)? ~ "{" ~ member* ~ "}" }
896            Rule::struct_def => {
897                let id = iter.next().unwrap().as_str().to_owned();
898                let key = id.clone();
899                let m1: Result<Vec<Vec<IdlStructMember>>, _> = iter
900                    .map(|p| {
901                        // skip the member-node and read sibbling directly
902                        self.read_struct_member(scope, p)
903                    })
904                    .collect();
905
906                let m2 = m1?;
907                let members = m2.into_iter().flatten().collect::<Vec<_>>();
908
909                let typedcl = IdlTypeDcl(IdlTypeDclKind::StructDcl(id, members));
910                self.add_type_dcl(scope, key, typedcl);
911                Ok(())
912            }
913            // union_def = { "union" ~ identifier ~ "switch" ~ "(" ~ switch_type_spec ~ ")" ~ "{" ~ switch_body ~ "}" }
914            Rule::union_def => {
915                let id = self.read_identifier(scope, iter.next().unwrap())?;
916                let key = id.to_owned();
917                let switch_type_spec = self.read_switch_type_spec(scope, iter.next().unwrap())?;
918                let switch_body = self.read_switch_body(scope, iter.next().unwrap())?;
919                let union_def =
920                    IdlTypeDcl(IdlTypeDclKind::UnionDcl(id, switch_type_spec, switch_body));
921
922                self.add_type_dcl(scope, key, union_def);
923                Ok(())
924            }
925            // type_declarator = { (template_type_spec | constr_type_dcl | simple_type_spec) ~ any_declarators }
926            Rule::type_declarator => {
927                let type_spec = self.read_type_spec(scope, iter.next().unwrap())?;
928
929                let any_declarators_pair = &iter.next().unwrap();
930
931                for p in any_declarators_pair.clone().into_inner() {
932                    let _ = self.process_declarator(scope, p, &type_spec);
933                }
934                Ok(())
935            }
936            // enum_dcl = { "enum" ~ identifier ~ "{" ~ enumerator ~ ("," ~ enumerator)* ~ ","? ~ "}" }
937            // enumerator = { identifier }
938            Rule::enum_dcl => {
939                let id = iter.next().unwrap().as_str().to_owned();
940                let key = id.clone();
941                let enums: Result<Vec<_>, pest::error::Error<Rule>> =
942                    iter.map(|p| self.read_identifier(scope, p)).collect();
943
944                let typedcl = IdlTypeDcl(IdlTypeDclKind::EnumDcl(id, enums?));
945                self.add_type_dcl(scope, key, typedcl);
946                Ok(())
947            }
948            // const_dcl = { "const" ~ const_type ~ identifier ~ "=" ~ const_expr }
949            Rule::const_dcl => {
950                let type_spec = self.read_type_spec(scope, iter.next().unwrap())?;
951                let id = self.read_identifier(scope, iter.next().unwrap())?;
952                let key = id.clone();
953                let const_expr = self.read_const_expr(scope, iter.next().unwrap())?;
954                let const_dcl = IdlConstDcl {
955                    id,
956                    typedcl: type_spec,
957                    value: const_expr,
958                };
959                self.add_const_dcl(scope, key, const_dcl);
960                Ok(())
961            }
962            // include_directive = !{ "#" ~ "include" ~ (("<" ~ path_spec ~ ">") | ("\"" ~ path_spec ~ "\"")) }
963            Rule::include_directive => {
964                if let Some(ref p) = pair.clone().into_inner().nth(0) {
965                    let fname = PathBuf::from(p.as_str());
966                    let data = loader
967                        .load(&fname)
968                        .map_err(|_| IdlError::FileNotFound(fname))?;
969
970                    let idl: Pairs<Rule> = IdlParser::parse(Rule::specification, &data)?;
971                    for p in idl {
972                        self.process::<L>(scope, loader, p)?;
973                    }
974                }
975                Ok(())
976            }
977            // anything else
978            _ => {
979                for p in iter {
980                    let _ = self.process::<L>(scope, loader, p);
981                }
982                Ok(())
983            }
984        }
985    }
986
987    // enum_dcl = { "enum" ~ identifier ~ "{" ~ enumerator ~ ("," ~ enumerator)* ~ ","? ~ "}" }
988    // enumerator = { identifier }
989}
990
991/// Provided w/ an object that supports writing, an IDL Loader, and an OMG Gen Config,
992/// generate Rust Types for the requested OMG IDL files.
993///
994/// @param out: An object that supports writing
995/// @param loader: Library Object to read IDL
996/// @param config: Library config
997fn generate_with_loader<W: Write, L: IdlLoader>(
998    out: &mut W,
999    loader: &mut L,
1000    config: &Configuration,
1001) -> Result<(), IdlError<Rule>> {
1002    let mut ctx = Context::new(config);
1003
1004    let idl_file = config.idl_file.clone();
1005    let idl_file_data = loader
1006        .load(&idl_file)
1007        .map_err(|_| IdlError::FileNotFound(idl_file))?;
1008
1009    let mut scope = Scope::new();
1010    let idl: Pairs<Rule> = IdlParser::parse(Rule::specification, &idl_file_data)?;
1011
1012    for p in idl {
1013        let _ = ctx.process::<L>(&mut scope, loader, p);
1014    }
1015
1016    let mut env = minijinja::Environment::new();
1017    minijinja_embed::load_templates!(&mut env);
1018    let root_module_text = ctx.root_module.render(&env, 0)?;
1019
1020    Ok(write!(out, "{root_module_text}")?)
1021}
1022
1023/// Object used to input the request IDL file into the library.
1024#[derive(Debug, Clone, Default)]
1025struct Loader {
1026    search_path: PathBuf,
1027}
1028
1029impl Loader {
1030    pub fn new(search_path: &Path) -> Self {
1031        Self {
1032            search_path: search_path.to_path_buf(),
1033        }
1034    }
1035}
1036
1037impl IdlLoader for Loader {
1038    /// Read the requested file and return as a Result<String>
1039    fn load(&self, filename: &Path) -> Result<String, io::Error> {
1040        let fullname = self.search_path.join(filename);
1041        let mut file = File::open(fullname)?;
1042        let mut data = String::new();
1043
1044        file.read_to_string(&mut data)?;
1045
1046        Ok(data)
1047    }
1048}
1049
1050/// Provided w/ an object that supports writing and a OMG Gen Config
1051/// generate Rust Types for the requested OMG IDL files.
1052///
1053/// @param out: An object that supports writing
1054/// @param config: Library config
1055pub fn generate_with_search_path<W: Write>(
1056    out: &mut W,
1057    config: &Configuration,
1058) -> Result<(), IdlError<Rule>> {
1059    let mut loader = Loader::new(&config.search_path);
1060    let mut env = minijinja::Environment::new();
1061    minijinja_embed::load_templates!(&mut env);
1062    generate_with_loader(out, &mut loader, config)
1063}