origen/framework/
aux_cmds.rs

1use super::extensions::ExtensionTOML;
2use super::helps::NOT_EXTENDABLE_MSG;
3use super::CommandTOML;
4use super::{build_commands, Command};
5use crate::commands::_prelude::*;
6use clap::Command as ClapCommand;
7use origen::core::config::AuxillaryCommandsTOML;
8use origen::{origen_config_metadata, Result, ORIGEN_CONFIG};
9use std::fs;
10use std::path::PathBuf;
11
12pub(crate) fn add_aux_ns_helps(helps: &mut CmdHelps, aux_cmds: &AuxCmds) {
13    for (ns, cmds) in aux_cmds.namespaces.iter() {
14        for (n, c) in cmds.commands.iter() {
15            helps.add_aux_cmd(ns, n).set_help_msg(&c.help);
16        }
17    }
18}
19
20#[inline]
21pub(crate) fn aux_ns_subcmd<'a>(
22    mut aux_sub: App,
23    helps: &'a CmdHelps,
24    aux_commands: &'a AuxCmds,
25    exts: &'a Extensions,
26) -> Result<App> {
27    for (ns, cmds) in aux_commands.namespaces.iter() {
28        let mut aux_sub_sub = ClapCommand::new(ns.clone())
29            .arg_required_else_help(true)
30            .after_help(NOT_EXTENDABLE_MSG);
31        if let Some(h) = cmds.help.as_ref() {
32            aux_sub_sub = aux_sub_sub.about(h.clone());
33        }
34        for top_cmd_name in cmds.top_commands.iter() {
35            aux_sub_sub = aux_sub_sub.subcommand(build_commands(
36                &cmds.commands.get(top_cmd_name).unwrap(),
37                &|cmd, app, opt_cache| exts.apply_to_aux_cmd(&ns, cmd, app, opt_cache),
38                &|cmd| cmds.commands.get(cmd).unwrap(),
39                &|cmd, app| helps.apply_helps(&CmdSrc::Aux(ns.to_string(), cmd.to_string()), app),
40            ));
41        }
42        aux_sub = aux_sub.subcommand(aux_sub_sub);
43    }
44    Ok(aux_sub)
45}
46
47#[derive(Debug, Deserialize)]
48pub(crate) struct CommandsToml {
49    pub help: Option<String>,
50    pub command: Option<Vec<CommandTOML>>,
51    pub extension: Option<Vec<ExtensionTOML>>,
52}
53
54#[derive(Default)]
55pub struct AuxCmds {
56    pub namespaces: IndexMap<String, AuxCmdNamespace>,
57}
58
59impl AuxCmds {
60    pub fn new(exts: &mut Extensions) -> Result<Self> {
61        let mut slf = Self::default();
62        if let Some(aux_cmds_configs) = ORIGEN_CONFIG.auxillary_commands.as_ref() {
63            for (i, config) in aux_cmds_configs.iter().enumerate() {
64                match AuxCmdNamespace::new(i, config, exts) {
65                    Ok(aux_ns) => {
66                        let ns = aux_ns.namespace().to_string();
67                        if let Some(existing_ns) = slf.namespaces.get(&ns) {
68                            log_error!("Auxillary commands namespaced '{}' already exists.", ns);
69                            log_error!(
70                                "Cannot add namespace from config '{}'",
71                                origen_config_metadata().aux_cmd_sources[i].display()
72                            );
73                            log_error!(
74                                "Namespace first defined in config '{}'",
75                                existing_ns.origin().display()
76                            );
77                        } else {
78                            slf.namespaces.insert(ns, aux_ns);
79                        }
80                    }
81                    Err(e) => {
82                        log_error!(
83                            "Unable to add auxillary commands at '{}' from config '{}'. The following error was met:",
84                            config.path().display(),
85                            origen_config_metadata().aux_cmd_sources[i].display()
86                        );
87                        log_error!("{}", e);
88                    }
89                }
90            }
91        }
92        Ok(slf)
93    }
94}
95
96pub struct AuxCmdNamespace {
97    commands: IndexMap<String, Command>,
98    pub top_commands: Vec<String>,
99    index: usize,
100    help: Option<String>,
101}
102
103impl AuxCmdNamespace {
104    fn _add_cmd(
105        slf: &mut Self,
106        current_path: String,
107        current_cmd: &mut CommandTOML,
108        parent_cmd: Option<&Command>,
109    ) -> Result<bool> {
110        if let Some(c) = Command::from_toml_cmd(
111            current_cmd,
112            CmdSrc::Aux(slf.namespace(), current_path.to_string()),
113            parent_cmd,
114        )? {
115            if let Some(ref mut sub_cmds) = current_cmd.subcommand {
116                for mut sub in sub_cmds {
117                    Self::_add_cmd(
118                        slf,
119                        format!("{}.{}", current_path, &sub.name),
120                        &mut sub,
121                        Some(&c),
122                    )?;
123                }
124            }
125            slf.commands.insert(current_path.clone(), c);
126            Ok(true)
127        } else {
128            Ok(false)
129        }
130    }
131
132    pub fn new(
133        index: usize,
134        config: &AuxillaryCommandsTOML,
135        exts: &mut Extensions,
136    ) -> Result<Self> {
137        let mut slf = Self {
138            commands: IndexMap::new(),
139            top_commands: vec![],
140            index: index,
141            help: None,
142        };
143
144        let mut commands_toml = PathBuf::from(&config.path);
145        if commands_toml.extension().is_none() {
146            commands_toml.set_extension("toml");
147        }
148
149        if commands_toml.exists() {
150            let content = match fs::read_to_string(&commands_toml) {
151                Ok(x) => x,
152                Err(e) => {
153                    bail!("{}", e);
154                }
155            };
156
157            let command_config: CommandsToml = match toml::from_str(&content) {
158                Ok(x) => x,
159                Err(e) => {
160                    bail!("Malformed commands.toml: {}", e);
161                }
162            };
163            slf.help = command_config.help.to_owned();
164
165            if let Some(commands) = command_config.command {
166                for mut cmd in commands {
167                    if Self::_add_cmd(&mut slf, cmd.name.to_owned(), &mut cmd, None)? {
168                        slf.top_commands.push(cmd.name.to_owned());
169                    }
170                }
171            }
172
173            if let Some(extensions) = command_config.extension {
174                for ext in extensions {
175                    match exts.add_from_aux_toml(&slf, ext) {
176                        Ok(_) => {}
177                        Err(e) => log_error!(
178                            "Failed to add extensions from aux commands '{}' ({}): {}",
179                            slf.namespace(),
180                            slf.path().display(),
181                            e
182                        ),
183                    }
184                }
185            }
186        } else {
187            bail!(
188                "Could not find auxillary commands file at '{}'",
189                commands_toml.display()
190            );
191        }
192        Ok(slf)
193    }
194
195    pub fn namespace(&self) -> String {
196        let config = &ORIGEN_CONFIG.auxillary_commands.as_ref().unwrap()[self.index];
197        if let Some(n) = config.name.as_ref() {
198            n.to_string()
199        } else {
200            format!(
201                "{}",
202                PathBuf::from(&config.path)
203                    .file_stem()
204                    .unwrap()
205                    .to_str()
206                    .unwrap()
207            )
208        }
209    }
210
211    pub fn path(&self) -> PathBuf {
212        PathBuf::from(&ORIGEN_CONFIG.auxillary_commands.as_ref().unwrap()[self.index].path)
213    }
214
215    pub fn root(&self) -> PathBuf {
216        self.path().with_extension("")
217    }
218
219    pub fn origin(&self) -> PathBuf {
220        origen_config_metadata().aux_cmd_sources[self.index].to_path_buf()
221    }
222}