origen/framework/
aux_cmds.rs1use 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}