yarig/
cfg.rs

1//! Configuration management for YARIG code generation.
2//!
3//! This module provides the configuration structures used to control the parsing and generation
4//! of register interface files (RIF). The configuration can be loaded from a TOML file and/or
5//! built from command-line arguments.
6//!
7//! # Overview
8//!
9//! The main entry point is the [`YarigCfg`] structure, which contains:
10//! - Input file specification and search paths
11//! - List of target formats to generate (SystemVerilog, C, Python, HTML, etc.)
12//! - Global settings (visibility, casing, parameters, etc.)
13//! - Target-specific configuration options
14//!
15//! # Usage
16//!
17//! ## Loading from TOML file and generate all targets
18//!
19//! ```no_run
20//! use yarig::cfg::YarigCfg;
21//! let cfg = YarigCfg::from_file("config.toml")
22//!     .expect("Failed to load configuration");
23//! cfg.gen_all()
24//!     .expect("Generation failed");
25//! ```
26//!
27//! # Configuration File Format
28//!
29//! The configuration file uses TOML format. Here's a minimal example:
30//!
31//! ```toml
32//! filename = "my_chip.rif"
33//! targets = ["sv", "c", "html"]
34//!
35//! [outputs]
36//! rtl = "./generated/rtl"
37//! c = "./generated/include"
38//! html = "./docs"
39//! ```
40//!
41//! For detailed configuration options, see the
42//! [configuration documentation](https://github.com/TheClams/rifgen/blob/dev/doc/config.md).
43//!
44//! # Supported Targets
45//!
46//! The [`RifGenTarget`] enum defines all available generation targets:
47//!
48//! - **Hardware**: SystemVerilog (`sv`), VHDL (`vhdl`)
49//! - **Software**: C headers (`c`), Python classes (`py`), UVM RAL (`ral`)
50//! - **Documentation**: HTML (`html`), LaTeX (`latex`), AsciiDoc (`adoc`), Framemaker (`mif`)
51//! - **Data formats**: JSON (`json`), SVD (`svd`), IP-XACT (`ipxact`)
52//!
53//! Each target has its own configuration structure (e.g., [`CfgHtml`], [`CfgC`], [`CfgRtl`])
54//! that allows fine-tuning the output for that specific target.
55
56use serde_derive::Deserialize;
57use std::{collections::HashMap, fs, path::PathBuf, str::FromStr};
58use toml;
59use crate::{
60    cli::RifGenArgs, comp::comp_inst::Comp, generator::{
61        casing::Casing, gen_adoc::GeneratorAdoc, gen_c::GeneratorC, gen_common::GeneratorBaseSetting, gen_html::GeneratorHtml, gen_ipxact::GeneratorIpXact, gen_json::GeneratorJson, gen_latex::GeneratorLatex, gen_mif::GeneratorMif, gen_py::{GeneratorPy, PyVersion}, gen_ral::GeneratorRal, gen_sv::GeneratorSv, gen_svd::GeneratorSvd, gen_vhdl::GeneratorVhdl, trait_doc::GeneratorDoc, trait_hw::GeneratorHw, trait_sw::GeneratorSw
62    }, parser::{parser_expr::ParamValues, ParserCfg, RifGenSrc, RsvdKeywordSel}, rifgen::{Interface, SuffixInfo}
63};
64
65#[derive(Debug, Clone, PartialEq)]
66pub enum RifGenTarget {
67    /// SystemVerilog
68    Sv,
69    /// Register Abstraction Layer (UVM)
70    Ral,
71    /// VHDL
72    Vhdl,
73    /// C Header
74    C,
75    /// Python Class
76    Py,
77    /// HTML documentation
78    Html,
79    /// Latex documentation
80    Latex,
81    /// Framemaker documentation
82    Mif,
83    /// SVD (System View Description)
84    Svd,
85    /// JSON
86    Json,
87    /// AsciiDoctor
88    Adoc,
89    /// IP-XACT
90    IpXact,
91    /// Custom target
92    Custom(String),
93}
94
95impl From<&str> for RifGenTarget {
96
97    fn from(s: &str) -> Self {
98        let s_lc = s.to_lowercase();
99        match s_lc.as_ref() {
100            "sv" => RifGenTarget::Sv,
101            "ral" => RifGenTarget::Ral,
102            "vhdl" => RifGenTarget::Vhdl,
103            "c" => RifGenTarget::C,
104            "py" => RifGenTarget::Py,
105            "html" => RifGenTarget::Html,
106            "tex" | "latex" => RifGenTarget::Latex,
107            "mif" => RifGenTarget::Mif,
108            "svd" => RifGenTarget::Svd,
109            "json" => RifGenTarget::Json,
110            "adoc" => RifGenTarget::Adoc,
111            "ipxact" => RifGenTarget::IpXact,
112            _ => RifGenTarget::Custom(s_lc),
113        }
114    }
115}
116
117impl std::fmt::Display for RifGenTarget {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        match self {
120            RifGenTarget::Sv        => write!(f, "SystemVerilog"),
121            RifGenTarget::Ral       => write!(f, "UVM Register Abstraction Layer"),
122            RifGenTarget::Vhdl      => write!(f, "VHDL"),
123            RifGenTarget::C         => write!(f, "C"),
124            RifGenTarget::Py        => write!(f, "Python"),
125            RifGenTarget::Html      => write!(f, "HTML"),
126            RifGenTarget::Latex     => write!(f, "LaTeX"),
127            RifGenTarget::Mif       => write!(f, "Framemaker MIF"),
128            RifGenTarget::Svd       => write!(f, "SVD"),
129            RifGenTarget::Json      => write!(f, "JSON"),
130            RifGenTarget::Adoc      => write!(f, "AsciiDoctor"),
131            RifGenTarget::IpXact    => write!(f, "IpXact"),
132            RifGenTarget::Custom(n) => write!(f, "'{n}'"),
133        }
134    }
135}
136
137impl<'de> serde::Deserialize<'de> for RifGenTarget {
138    fn deserialize<D: serde::Deserializer<'de> >(d: D) -> Result<Self, D::Error> {
139        let s = String::deserialize(d)?;
140        Ok(RifGenTarget::from(s.as_ref()))
141    }
142}
143
144impl RifGenTarget {
145    pub fn custom_name(&self) -> Option<String> {
146        if let RifGenTarget::Custom(n) = self {
147            Some(n.to_owned())
148        } else {
149            None
150        }
151    }
152}
153
154/// Main configuration structure for YARIG code generation.
155///
156/// This structure holds all configuration options for parsing RIF files and generating
157/// various output targets. It can be created from a TOML configuration file using
158/// [`YarigCfg::from_file`], from command-line arguments using [`YarigCfg::from_cli`],
159/// or constructed manually.
160///
161/// # Fields
162///
163/// ## Input Configuration
164/// - `filename`: Path to the main RIF file to compile
165/// - `path`: Reference path for resolving relative paths (auto-detected from config file)
166/// - `include`: List of directories to search for included RIF files
167///
168/// ## Generation Control
169/// - `targets`: List of output formats to generate (e.g., sv, c, html)
170/// - `gen_inc`: List of included components to generate (use `["*"]` for all)
171/// - `local`: Components to generate locally (relative to their RIF file location)
172/// - `subdir`: Subdirectory name for non-top-level generated files
173///
174/// ## Global Settings
175/// - `public`: Generate only public registers/fields (hide private ones)
176/// - `interface`: Override the HDL interface type (e.g., APB, AXI)
177/// - `parameters`: Parameter values to override defaults in RIF files
178/// - `casing`: Default casing convention for identifiers
179/// - `keywords`: Reserved keyword handling configuration
180/// - `suffixes`: Suffix definitions for parameterized components
181/// - `suffix_rtl_only`: Apply suffixes only to RTL targets
182/// - `auto_legacy`: Use legacy order for interrupt registers
183///
184/// ## Output Configuration
185/// - `outputs`: Map of target names to output directories
186///
187/// ## Target-Specific Settings
188/// - `html`, `adoc`, `latex`, `mif`: Documentation generator settings
189/// - `c`: C header generator settings
190/// - `py`: Python class generator settings
191/// - `rtl`: RTL (SystemVerilog/VHDL) generator settings
192/// - `ral`: UVM RAL generator settings
193/// - `json`, `svd`, `ipxact`: Data format generator settings
194///
195/// # Example
196///
197/// ```no_run
198/// # use yarig::cfg::YarigCfg;
199/// // Load from TOML file
200/// let cfg = YarigCfg::from_file("config.toml").unwrap();
201///
202/// // Parse RIF and generate all configured targets
203/// let (component, paths) = cfg.gen_all().unwrap();
204/// ```
205#[derive(Deserialize, Debug, Default)]
206#[serde(default)]
207pub struct YarigCfg {
208    /// File name of the RIF to compile
209    pub filename: String,
210    /// Path used as reference for relative paths
211    pub path: Option<String>,
212    /// List of path to search for included reference
213    pub include: Vec<String>,
214    /// List of included reference to generate (use ["*"] for all)
215    pub gen_inc: Vec<String>,
216    /// Sub-directory name for generated file which are not the top level
217    pub subdir: Option<String>,
218    /// List of included reference which must be generated locally (use ["*"] to match all component in the gen_inc definition)
219    pub local: Vec<String>,
220    /// List of targets to generate
221    pub targets: Vec<RifGenTarget>,
222    /// Flag when the output is for public usage (i.e. hide private register/field)
223    pub public: bool,
224    /// Specify an HDL interface
225    pub interface: Option<Interface>,
226    /// dictionary of parameters
227    pub parameters: HashMap<String,isize>,
228    /// dictionary of path associated to each targets
229    pub outputs: HashMap<String,String>,
230    /// Use legacy order for interrupts (mask before enable)
231    pub auto_legacy: bool,
232    /// Use suffix only for RTL generation
233    pub suffix_rtl_only: bool,
234    /// optional suffix definition
235    pub suffixes: HashMap<String, SuffixInfo>,
236    /// Specify casing used in all targets
237    pub casing: Option<Casing>,
238    /// Specify reserved keywords
239    pub keywords: RsvdKeywordSel,
240    //-- Target specific settings--//
241    pub html  : CfgHtml,
242    pub adoc  : CfgAdoc,
243    pub c  : CfgC,
244    pub ral: CfgRal,
245    pub rtl: CfgRtl,
246    pub py : CfgPy,
247    pub json : CfgJson,
248    pub svd: CfgSvd,
249    pub ipxact: CfgIpXact,
250    pub mif: CfgMif,
251    pub latex: CfgLatex,
252}
253
254/// Configuration specific to HTML target
255#[derive(Deserialize, Debug, Clone, Default)]
256#[serde(deny_unknown_fields)]
257pub struct CfgHtml {
258    /// Specify a file for the CSS template
259    pub css: Option<String>,
260    /// Split the HTML output in multiple files (one perf RIF)
261    pub split: Option<bool>,
262    /// Casing for register and field name
263    pub casing: Option<Casing>,
264    /// Sub-directory name for generated file which are not the top level
265    pub subdir: Option<String>,
266}
267
268/// Configuration specific to ASCII Doc target
269#[derive(Deserialize, Debug, Clone, Default)]
270#[serde(deny_unknown_fields)]
271pub struct CfgAdoc {
272    /// Split the AsciiDoc output in multiple files (one perf RIF)
273    pub split: Option<bool>,
274    /// List of included reference to generate (use ["*"] for all): valid only if split is enabled
275    pub gen_inc: Option<Vec<String>>,
276    /// List of included reference which must be generated locally (use ["*"] to match all component in the gen_inc definition)
277    pub local: Option<Vec<String>>,
278    /// Casing for register and field name
279    pub casing: Option<Casing>,
280    /// Sub-directory name for generated file which are not the top level
281    pub subdir: Option<String>,
282}
283
284/// Configuration specific to LaTeX target
285#[derive(Deserialize, Debug, Clone, Default)]
286#[serde(deny_unknown_fields)]
287pub struct CfgLatex {
288    /// Casing for register and field name
289    pub casing: Option<Casing>,
290    /// Split the output in multiple files (one per RIF)
291    pub split: Option<bool>,
292    /// Sub-directory name for generated file which are not the top level
293    pub subdir: Option<String>,
294}
295
296/// Configuration specific to C target
297#[derive(Deserialize, Debug, Clone, Default)]
298#[serde(deny_unknown_fields)]
299pub struct CfgC {
300    /// Name of base address offset (default to PERIPH_BASE_ADDR)
301    pub base_offset: Option<String>,
302    /// List of included reference to generate (use ["*"] for all)
303    pub gen_inc: Option<Vec<String>>,
304    /// List of included reference which must be generated locally (use ["*"] to match all component in the gen_inc definition)
305    pub local: Option<Vec<String>>,
306    /// Sub-directory name for generated file which are not the top level
307    pub subdir: Option<String>,
308}
309
310/// Configuration specific to RTL target
311#[derive(Deserialize, Debug, Clone, Default)]
312#[serde(deny_unknown_fields)]
313pub struct CfgRtl {
314    /// Number of pipe level for register access (default 1 on the read value)
315    pub nb_pipe: Option<u8>,
316    /// List of included reference to generate (use ["*"] for all)
317    pub gen_inc: Option<Vec<String>>,
318    /// List of included reference which must be generated locally (use ["*"] to match all component in the gen_inc definition)
319    pub local: Option<Vec<String>>,
320    /// Generate constant for register address in the package
321    pub const_reg: Option<bool>,
322    /// Generate constant for field reset/position/width
323    pub const_field: Option<bool>,
324    /// Sub-directory name for generated file which are not the top level
325    pub subdir: Option<String>,
326}
327
328/// Configuration specific to RAL target
329#[derive(Deserialize, Debug, Clone, Default)]
330#[serde(deny_unknown_fields)]
331pub struct CfgRal {
332    /// Name of the register block class (default to uvm_reg_block)
333    pub class: Option<String>,
334    /// Name of the macro use to instantiate the RAL (default to an internal macro ral_create_reg_block)
335    pub macro_name: Option<String>,
336    /// List of included reference to generate (use ["*"] for all)
337    pub gen_inc: Option<Vec<String>>,
338    /// List of included reference which must be generated locally (use ["*"] to match all component in the gen_inc definition)
339    pub local: Option<Vec<String>>,
340    /// List of optional imports for regsiter block
341    pub imports: Option<HashMap<String,String>>,
342    /// Sub-directory name for generated file which are not the top level
343    pub subdir: Option<String>,
344}
345
346/// Configuration specific to python target
347#[derive(Deserialize, Debug, Clone, Default)]
348#[serde(deny_unknown_fields)]
349pub struct CfgPy {
350    /// Name of python class definition (Peripheral/Register/Field)
351    pub class: Option<String>,
352    /// Python version
353    pub version: Option<PyVersion>,
354    /// Create an __init__.py file at the RIF output location
355    pub init_file: Option<bool>,
356    /// List of included reference to generate (use ["*"] for all)
357    pub gen_inc: Option<Vec<String>>,
358    /// List of included reference which must be generated locally (use ["*"] to match all component in the gen_inc definition)
359    pub local: Option<Vec<String>>,
360    /// Sub-directory name for generated file which are not the top level
361    pub subdir: Option<String>,
362    /// Directory where regmap.py file is generated
363    pub regmap_dir: Option<String>,
364}
365
366/// Configuration specific to JSON target
367#[derive(Deserialize, Debug, Clone, Default)]
368#[serde(deny_unknown_fields)]
369pub struct CfgJson {
370    /// List of included reference to generate (use ["*"] for all)
371    pub gen_inc: Option<Vec<String>>,
372    /// List of included reference which must be generated locally (use ["*"] to match all component in the gen_inc definition)
373    pub local: Option<Vec<String>>,
374    /// Sub-directory name for generated file which are not the top level
375    pub subdir: Option<String>,
376}
377
378/// Configuration specific to SVD target
379#[derive(Deserialize, Debug, Clone, Default)]
380#[serde(deny_unknown_fields)]
381pub struct CfgSvd {
382    /// IP vendor
383    pub vendor: Option<String>,
384    /// IP version
385    pub version: Option<String>,
386}
387
388/// Configuration specific to IP Xact target
389#[derive(Deserialize, Debug, Clone, Default)]
390#[serde(deny_unknown_fields)]
391pub struct CfgIpXact {
392    /// IP vendor
393    pub vendor: Option<String>,
394    /// IP library
395    pub library: Option<String>,
396    /// IP version
397    pub version: Option<String>,
398    /// Sub-directory name for generated file which are not the top level
399    pub subdir: Option<String>,
400}
401
402/// Configuration specific to MIF target
403#[derive(Deserialize, Debug, Clone, Default)]
404#[serde(deny_unknown_fields)]
405pub struct CfgMif {
406    /// Split the MIF output in multiple files
407    pub split              : Option<bool>,
408    /// Style of anchor paragraph
409    pub anchor             : Option<String>,
410    /// Style of Header level 2
411    pub h2                 : Option<String>,
412    /// Style of Header level 3
413    pub h3                 : Option<String>,
414    /// Style of Table title
415    pub table_title        : Option<String>,
416    /// Style of Table heading
417    pub table_heading      : Option<String>,
418    /// Style of Table cell
419    pub table_cell         : Option<String>,
420    /// Style of Table for Rifmux
421    pub table_kind_rifmux  : Option<String>,
422    /// Style of Table for address mapping
423    pub table_kind_mapping : Option<String>,
424    /// Style of Table for register definitino
425    pub table_kind_reg     : Option<String>,
426    /// Dimension of the table for Rifmux, Pages (3 columns)
427    pub width_3col     : Option<[f32;3]>,
428    /// Dimension of the table for registers (4 columns)
429    pub width_4col     : Option<[f32;4]>,
430    /// Dimension of the table for fields (5 columns)
431    pub width_5col     : Option<[f32;5]>,
432    /// List of included reference to generate (use ["*"] for all)
433    pub gen_inc: Option<Vec<String>>,
434    /// List of included reference which must be generated locally (use ["*"] to match all component in the gen_inc definition)
435    pub local: Option<Vec<String>>,
436    /// Casing for register and field name
437    pub casing: Option<Casing>,
438    /// Sub-directory name for generated file which are not the top level
439    pub subdir: Option<String>,
440}
441
442impl FromStr for YarigCfg {
443    type Err = String;
444
445    fn from_str(s: &str) -> Result<Self, Self::Err> {
446       toml::from_str(s).map_err(|e| format!("Error parsing configuration: {:?}", e.message()))
447    }
448}
449
450impl YarigCfg {
451
452    /// Creates a configuration from a TOML file.
453    ///
454    /// This method reads the TOML configuration file, parses it, and automatically sets
455    /// the `path` field to the directory containing the configuration file. This path
456    /// is used to resolve relative paths in the configuration.
457    ///
458    /// # Arguments
459    ///
460    /// * `path` - Path to the TOML configuration file
461    ///
462    /// # Returns
463    ///
464    /// Returns `Ok(YarigCfg)` on success, or `Err(String)` with an error message on failure.
465    ///
466    pub fn from_file<T : Into<PathBuf>>(path: T) -> Result<YarigCfg,String> {
467        let path: PathBuf = path.into();
468        let s = fs::read_to_string(&path)
469            .map_err(|e| format!("Error opening {path:?} : {e:?}"))?;
470        let mut cfg = Self::from_str(&s)?;
471        if let Some(d) = path.parent() {
472            if let Ok(d) = fs::canonicalize(d) {
473                cfg.path = d.to_str().map(str::to_string);
474            }
475        }
476        Ok(cfg)
477    }
478
479    /// Creates a configuration from command-line arguments.
480    ///
481    /// If a configuration file is specified in the arguments, it is loaded first, then
482    /// any command-line options override the file settings. If no configuration file
483    /// is specified, starts with default settings.
484    ///
485    /// # Arguments
486    ///
487    /// * `args` - Command-line arguments structure
488    ///
489    /// # Returns
490    ///
491    /// Returns `Ok(YarigCfg)` on success, or `Err(String)` with an error message on failure.
492    ///
493    pub fn from_cli(args: RifGenArgs) -> Result<YarigCfg,String> {
494        let mut cfg = if let Some(cfg_path) = &args.cfg {
495            YarigCfg::from_file(cfg_path)?
496        } else {
497                YarigCfg::default()
498        };
499        cfg.update_with_cli(args);
500        Ok(cfg)
501    }
502
503    pub fn update_with_cli(&mut self, args: RifGenArgs) {
504        if let Some(fname) = args.rif {self.filename = fname.to_owned()};
505        if !args.include.is_empty() {self.include = args.include.clone()};
506        if !args.gen_inc.is_empty() {self.gen_inc = args.gen_inc.clone()};
507        if !args.targets.is_empty() {self.targets = args.targets.to_owned()};
508        if args.public {self.public = true};
509        if args.auto_legacy {self.auto_legacy = true};
510        // Clear target if check is enabled and override target if at least one is defined on the command-line
511        if args.check {self.targets.clear();}
512        else if !args.targets.is_empty() {self.targets = args.targets.to_owned()};
513        //
514        if !args.parameters.is_empty() {self.parameters.extend(args.parameters)};
515        if let Some(suffix) = args.suffix {self.suffixes.insert("".to_owned(), suffix);};
516        if args.py_version.is_some() {self.py.version = args.py_version};
517        if args.casing.is_some() {self.casing = args.casing};
518        if args.subdir.is_some() {self.subdir = args.subdir.clone()};
519        if args.interface.is_some() {self.interface = args.interface};
520        if args.suffix_rtl_only {self.suffix_rtl_only = true;}
521        if args.rtl_const_reg {self.rtl.const_reg = Some(true);}
522        if args.rtl_const_field {self.rtl.const_field = Some(true);}
523        if args.keyword_rename {self.keywords.error = false;}
524        if args.targets.contains(&RifGenTarget::Sv) {self.keywords.sv = true;}
525        if args.targets.contains(&RifGenTarget::Vhdl) {self.keywords.vhdl = true;}
526
527        for t in args.split.iter() {
528            match t {
529                RifGenTarget::Html => self.html.split = Some(true),
530                RifGenTarget::Adoc => self.adoc.split = Some(true),
531                RifGenTarget::Mif  => self.mif.split  = Some(true),
532                _ => eprintln!("Split target {t} not supported ! Expecting html, adoc, mif."),
533            }
534        }
535
536        //
537        let outputs = [
538            ("c"  , args.output_c),
539            ("py" , args.output_py),
540            ("doc", args.output_doc),
541            ("json", args.output_json),
542            ("rtl", args.output_rtl),
543            ("sim", args.output_sim)];
544        for (k,v) in outputs.iter() {
545            if let Some(path) = v {
546                self.outputs.insert(k.to_string(), path.to_owned());
547            }
548        }
549    }
550
551    /// Retrieve output path definition from the configuration given a target
552    pub fn get_output_path(&self, target: &RifGenTarget) -> (PathBuf, Option<String>, String) {
553        let (keys,def) = match target {
554            RifGenTarget::C => (["c", "sw"],"c"),
555            RifGenTarget::Html => (["html", "doc"],"doc"),
556            RifGenTarget::Mif => (["mif", "doc"], "doc"),
557            RifGenTarget::Latex => (["latex", "doc"], "doc"),
558            RifGenTarget::Sv => (["sv", "rtl"], "rtl"),
559            RifGenTarget::Vhdl => (["vhdl", "rtl"], "rtl"),
560            RifGenTarget::Ral => (["ral", "sim"], "sim"),
561            RifGenTarget::Py => (["py", "sw"], "py"),
562            RifGenTarget::Json => (["json", "doc"], "doc"),
563            RifGenTarget::Adoc => (["adoc", "doc"], "doc"),
564            RifGenTarget::Svd => (["svd", "sw"], "sw"),
565            RifGenTarget::IpXact => (["ipxact", "sw"], "sw"),
566            RifGenTarget::Custom(n) => ([n.as_str(),n.as_str()], n.as_str()),
567        };
568        self.get_output_path_kd(&keys, def)
569    }
570
571    /// Retrieve output path definition from the configuration given an array of keys and a default
572    pub fn get_output_path_kd(&self, keys: &[&str], def: &str) -> (PathBuf, Option<String>, String) {
573        let mut path = format!("./{def}");
574        for k in keys.iter() {
575            if let Some(p) = self.outputs.get(*k) {
576                path = p.to_owned();
577                break;
578            }
579        }
580        let is_rel = path.starts_with('.') || !path.contains('/');
581        let path_buf : PathBuf = match (&self.path, is_rel) {
582            (Some(cwd), true) => [cwd, &path].iter().collect(),
583            _ => path.clone().into()
584        };
585
586        match (path_buf.extension().is_some(),path_buf.file_name()) {
587            (true, Some(name)) => {
588                let fname = name.to_string_lossy().to_string();
589                let parent = path_buf.parent().unwrap_or(&path_buf).to_path_buf();
590                (parent, Some(fname), path)
591            }
592            _ => (path_buf, None, path)
593        }
594    }
595
596    pub fn get_local(&self, target: &RifGenTarget) -> Option<&Vec<String>> {
597        let local = match target {
598            RifGenTarget::C    => self.c.local.as_ref(),
599            RifGenTarget::Mif  => self.mif.local.as_ref(),
600            RifGenTarget::Sv   |
601            RifGenTarget::Vhdl => self.rtl.local.as_ref(),
602            RifGenTarget::Ral  => self.ral.local.as_ref(),
603            RifGenTarget::Py   => self.py.local.as_ref(),
604            RifGenTarget::Adoc => self.adoc.local.as_ref(),
605            _ => None
606        };
607        if local.is_none() {
608            Some(&self.local)
609        } else {
610            local
611        }
612    }
613
614    pub fn get_subdir(&self, target: &RifGenTarget) -> Option<&String> {
615        let subdir = match target {
616            RifGenTarget::Html   => self.html.subdir.as_ref(),
617            RifGenTarget::Adoc   => self.adoc.subdir.as_ref(),
618            RifGenTarget::C      => self.c.subdir.as_ref(),
619            RifGenTarget::Ral    => self.ral.subdir.as_ref(),
620            RifGenTarget::Sv     |
621            RifGenTarget::Vhdl   => self.rtl.subdir.as_ref(),
622            RifGenTarget::Py     => self.py.subdir.as_ref(),
623            RifGenTarget::Json   => self.json.subdir.as_ref(),
624            RifGenTarget::IpXact => self.ipxact.subdir.as_ref(),
625            RifGenTarget::Mif    => self.mif.subdir.as_ref(),
626            RifGenTarget::Latex  => self.latex.subdir.as_ref(),
627            _ => None
628        };
629        if subdir.is_none() {
630            self.subdir.as_ref()
631        } else {
632            subdir
633        }
634    }
635
636    /// Parses the RIF file and generates all configured output targets.
637    ///
638    /// This is the main method that orchestrates the entire generation process:
639    /// 1. Resolves the RIF file path (using configured paths and includes)
640    /// 2. Parses the RIF file and any included files
641    /// 3. Compiles the RIF into an internal representation
642    /// 4. Applies parameter overrides and suffix settings
643    /// 5. Generates output for each configured target
644    ///
645    /// # Returns
646    ///
647    /// Returns a tuple containing:
648    /// - `Comp`: The compiled component structure (RIF or RifMux)
649    /// - `HashMap<String, PathBuf>`: Map of component names to their file paths
650    ///
651    /// # Errors
652    ///
653    /// Returns an error if:
654    /// - The RIF file cannot be found or read
655    /// - Parsing fails (syntax errors in the RIF file)
656    /// - Compilation fails (semantic errors, type mismatches, etc.)
657    /// - Any target generator fails to produce output
658    ///
659    pub fn gen_all(&self) -> Result<(Comp, HashMap<String, PathBuf>), String> {
660
661        let mut params = ParamValues::new();
662        self.parameters.iter().for_each(
663            |(k,v)| params.insert(k.to_owned(), *v)
664        );
665        // if !params.is_empty() {println!("Parameters: {params}");}
666        let mut rif_path : PathBuf = self.filename.clone().into();
667        if !rif_path.exists() && rif_path.is_relative() && self.path.is_some() {
668            rif_path = [self.path.as_ref().unwrap(), &self.filename].iter().collect();
669        }
670        let parser_cfg = ParserCfg::new(self.keywords, self.auto_legacy);
671        let rif_src = RifGenSrc::from_file(&rif_path, &self.include, &parser_cfg)
672            .map_err(|e| format!("Parsing Error : {e}"))?;
673        let mut rif_obj = Comp::compile(&rif_src, &self.suffixes, &params)
674            .map_err(|e| format!("Compilation failed: {e}"))?;
675        // Handle case where suffixes are enabled only for RTL targets
676        // Force to None by default and will set it properly only in the appropriate target
677        let no_suffixes = HashMap::new();
678        if self.suffix_rtl_only {
679            rif_obj.set_suffixes(&no_suffixes);
680        }
681        // Force interface if specified in the configuration
682        if let Some(intf) = &self.interface {
683            rif_obj.set_interface(intf);
684        }
685        //
686        let base_setting = GeneratorBaseSetting::new(self.casing, self.public, &self.gen_inc, &self.subdir);
687        for target in self.targets.iter() {
688            let mut setting = base_setting.clone();
689            let out = self.get_output_path(target);
690            setting.set_output((out.0, out.1));
691            if let Some(local) = self.get_local(target) {
692                setting.set_locals(target, local, &rif_src.paths, &out.2);
693            }
694            if let Some(subdir) = self.get_subdir(target) {
695                setting.subdir = Some(subdir.clone());
696            }
697            match target {
698                RifGenTarget::C => {
699                    let mut g = GeneratorC::new(setting, self.c.clone());
700                    g.gen_all(&rif_obj).map_err(|e| format!("C generation failed: {e}"))?;
701                },
702                RifGenTarget::Html => {
703                    if let Some(split) = self.html.split {setting.split = split;}
704                    let mut g = GeneratorHtml::new(setting, self.html.clone());
705                    g.gen_all(&rif_obj).map_err(|e| format!("Html generation failed: {e}"))?;
706                }
707                RifGenTarget::Mif => {
708                    let mut g = GeneratorMif::new(setting, self.mif.clone());
709                    g.gen_all(&rif_obj).map_err(|e| format!("MIF generation failed: {e}"))?;
710                }
711                RifGenTarget::Latex => {
712                    if let Some(split) = self.latex.split {setting.split = split;}
713                    if let Some(casing) = self.latex.casing {setting.casing = casing;}
714                    let mut g = GeneratorLatex::new(setting);
715                    g.gen_all(&rif_obj).map_err(|e| format!("Latex generation failed: {e}"))?;
716                }
717                RifGenTarget::Sv => {
718                    if self.suffix_rtl_only {
719                        rif_obj.set_suffixes(&self.suffixes);
720                    }
721                    let mut g = GeneratorSv::new(setting, self.rtl.clone());
722                    g.gen_all(&rif_obj).map_err(|e| format!("SystemVerilog generation failed: {e}"))?;
723                    if self.suffix_rtl_only {
724                        rif_obj.set_suffixes(&no_suffixes);
725                    }
726                }
727                RifGenTarget::Vhdl => {
728                    if self.suffix_rtl_only {
729                        rif_obj.set_suffixes(&self.suffixes);
730                    }
731                    let mut g = GeneratorVhdl::new(setting, self.rtl.clone());
732                    g.gen_all(&rif_obj).map_err(|e| format!("VHDL generation failed: {e}"))?;
733                    if self.suffix_rtl_only {
734                        rif_obj.set_suffixes(&no_suffixes);
735                    }
736                }
737                RifGenTarget::Ral => {
738                    let mut g = GeneratorRal::new(setting, self.ral.clone());
739                    g.gen_all(&rif_obj).map_err(|e| format!("RAL generation failed: {e}"))?;
740                }
741                RifGenTarget::Py => {
742                    let mut g = GeneratorPy::new(setting, self.py.clone());
743                    g.gen_all(&rif_obj).map_err(|e| format!("Python generation failed: {e}"))?;
744                }
745                RifGenTarget::Json => {
746                    let mut g = GeneratorJson::new(setting);
747                    g.gen_all(&rif_obj).map_err(|e| format!("JSON generation failed: {e}"))?;
748                }
749                RifGenTarget::Adoc => {
750                    let mut g = GeneratorAdoc::new(setting, self.adoc.clone());
751                    g.gen_all(&rif_obj).map_err(|e| format!("AsciiDoctor generation failed: {e}"))?;
752                }
753                RifGenTarget::Svd => {
754                    let mut g = GeneratorSvd::new(setting, self.svd.clone());
755                    g.gen_all(&rif_obj).map_err(|e| format!("SVD generation failed: {e}"))?;
756                }
757                RifGenTarget::IpXact => {
758                    let mut g = GeneratorIpXact::new(setting, self.ipxact.clone());
759                    g.gen_all(&rif_obj).map_err(|e| format!("IP XACT generation failed: {e}"))?;
760                }
761                _ => {},
762            }
763        }
764        Ok((rif_obj, rif_src.paths))
765    }
766
767}