1use super::extensions::ExtensionSource;
2use crate::commands::_prelude::*;
3use origen_metal::indexmap::IndexSet;
4use std::collections::HashMap;
5use std::fmt;
6
7pub const NOT_EXTENDABLE_MSG: &'static str = "This command does not support extensions.";
8
9#[derive(Debug)]
10pub struct CmdHelps {
11 helps: HashMap<CmdSrc, CmdHelp>,
12}
13
14impl CmdHelps {
15 pub fn new() -> Self {
16 Self {
17 helps: HashMap::new(),
18 }
19 }
20
21 pub fn core_cmd(&self, cmd: &str) -> Command {
22 self.apply_core_cmd_helps(cmd, Command::new(cmd.to_string()))
23 }
24
25 pub fn core_subc(&self, cmd_path: &[&str]) -> Command {
26 self.apply_core_subc_helps(cmd_path, Command::new(cmd_path.last().unwrap().to_string()))
27 }
28
29 pub fn add_core_cmd(&mut self, cmd_name: &str) -> &mut CmdHelp {
30 self.helps
31 .entry(CmdSrc::Core(cmd_name.to_string()))
32 .or_default()
33 }
34
35 pub fn add_core_sub_cmd(&mut self, cmd_path: &[&str]) -> &mut CmdHelp {
36 self.helps
37 .entry(CmdSrc::Core(cmd_path.join(".")))
38 .or_default()
39 }
40
41 pub fn add_app_cmd(&mut self, cmd_name: &str) -> &mut CmdHelp {
42 self.helps
43 .entry(CmdSrc::App(cmd_name.to_string()))
44 .or_default()
45 }
46
47 pub fn add_pl_cmd(&mut self, pl_name: &str, cmd_name: &str) -> &mut CmdHelp {
48 self.helps
49 .entry(CmdSrc::Plugin(pl_name.to_string(), cmd_name.to_string()))
50 .or_default()
51 }
52
53 pub fn add_aux_cmd(&mut self, ns: &str, cmd_name: &str) -> &mut CmdHelp {
54 self.helps
55 .entry(CmdSrc::Aux(ns.to_string(), cmd_name.to_string()))
56 .or_default()
57 }
58
59 pub fn apply_core_cmd_helps<'a>(&'a self, cmd_name: &str, app: Command) -> Command {
60 self.apply_helps(&CmdSrc::Core(cmd_name.to_string()), app)
61 }
62
63 pub fn apply_core_subc_helps<'a>(&'a self, cmd_path: &[&str], app: Command) -> Command {
64 self.apply_helps(&CmdSrc::Core(cmd_path.join(".")), app)
65 }
66
67 pub fn apply_helps<'a>(&'a self, cmd_src: &CmdSrc, mut app: Command) -> Command {
68 if let Some(helps) = self.helps.get(cmd_src) {
69 if let Some(h) = helps.before_help.as_ref() {
70 app = app.before_help(h.clone());
71 }
72 if let Some(h) = helps.help.as_ref() {
73 app = app.about(h.clone());
74 }
75 if let Some(h) = helps.after_help.as_ref() {
76 app = app.after_help(h.clone());
77 }
78 } else {
79 log_error!(
80 "Could not apply help messages to {} - no such command found",
81 cmd_src
82 );
83 }
84 app
85 }
86
87 pub fn apply_exts(&mut self, extensions: &Extensions) {
88 for (target, exts) in extensions.exts() {
89 if let Some(help) = self.helps.get_mut(&target) {
90 if !help.extendable {
91 log_error!("Command '{}' does not support extensions but an extension was attempted from:", target);
92 for ext in exts {
93 log_error!("\t{}", ext.source);
94 }
95 continue;
96 }
97
98 let mut extended_from_app = false;
99 let mut pls: IndexSet<&str> = IndexSet::new();
100 let mut nspaces: IndexSet<&str> = IndexSet::new();
101 for ext in exts.iter() {
102 match ext.source {
103 ExtensionSource::App => extended_from_app = true,
104 ExtensionSource::Plugin(ref n) => {
105 pls.insert(n);
106 }
107 ExtensionSource::Aux(ref n, _) => {
108 nspaces.insert(n);
109 }
110 }
111 }
112 let mut msg = "This command is extended from:".to_string();
113 if extended_from_app {
114 msg += "\n - the App";
115 }
116 if !pls.is_empty() {
117 msg += &format!(
118 "\n - Plugins: {}",
119 pls.iter()
120 .map(|n| format!("'{}'", n))
121 .collect::<Vec<String>>()
122 .join(", ")
123 );
124 }
125 if !nspaces.is_empty() {
126 msg += &format!(
127 "\n - Aux Namespaces: {}",
128 nspaces
129 .iter()
130 .map(|n| format!("'{}'", n))
131 .collect::<Vec<String>>()
132 .join(", ")
133 );
134 }
135 if let Some(after) = help.after_help.as_ref() {
136 help.after_help = Some(after.to_string() + "\n\n" + &msg);
137 } else {
138 help.after_help = Some(msg);
139 }
140 } else {
141 log_error!("Tried to extend unknown command '{}' from:", target);
142 for ext in exts {
143 log_error!("\t{}", ext.source);
144 }
145 }
146 }
147 }
148}
149
150#[derive(Debug)]
151pub struct CmdHelp {
152 help: Option<String>,
153 after_help: Option<String>,
154 before_help: Option<String>,
155 extendable: bool,
156}
157
158impl Default for CmdHelp {
159 fn default() -> Self {
160 Self {
161 help: None,
162 after_help: None,
163 before_help: None,
164 extendable: true,
165 }
166 }
167}
168
169impl CmdHelp {
170 pub fn set_help_msg(&mut self, help_msg: &str) -> &mut Self {
171 self.help = Some(help_msg.to_string());
172 self
173 }
174
175 pub fn set_as_not_extendable(&mut self) -> &mut Self {
176 self.extendable = false;
177 if let Some(h) = self.after_help.as_mut() {
178 self.after_help = Some(format!("{h}\n\n{NOT_EXTENDABLE_MSG}"));
179 } else {
180 self.after_help = Some(NOT_EXTENDABLE_MSG.to_string());
181 }
182 self
183 }
184}
185
186#[derive(Debug, Hash, Eq, PartialEq)]
187pub enum CmdSrc {
188 Core(String), App(String), Plugin(String, String), Aux(String, String), }
193
194impl CmdSrc {
195 pub fn new(target: &str) -> Result<Self> {
196 let (scope, t) = target
197 .split_once('.')
198 .ok_or_else(|| format!("Could not discern scope from '{}'", target))?;
199 Ok(match scope {
200 "origen" => Self::Core(t.to_string()),
201 "app" => Self::App(t.to_string()),
202 "plugin" => {
203 let (pl_name, pl_t) = t
204 .split_once('.')
205 .ok_or_else(|| format!("Could not discern plugin from '{}'", t))?;
206 Self::Plugin(pl_name.to_string(), pl_t.to_string())
207 }
208 "aux" | "aux_ns" => {
209 let (ns_name, aux_t) = t.split_once('.').ok_or_else(|| {
210 format!("Could not discern auxillary command namespace from '{}'", t)
211 })?;
212 Self::Aux(ns_name.to_string(), aux_t.to_string())
213 }
214 _ => bail!(
215 "Unknown target scope '{}'. Expected 'origen', 'app', 'aux', or 'plugin'",
216 scope
217 ),
218 })
219 }
220
221 pub fn offset_path(&self) -> &str {
222 match self {
223 Self::Core(cmd) | Self::App(cmd) => &cmd,
224 Self::Plugin(_, cmd) | Self::Aux(_, cmd) => &cmd,
225 }
226 }
227}
228
229impl fmt::Display for CmdSrc {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 match self {
232 Self::Core(cmd) => {
233 write!(f, "origen.{}", cmd)
234 }
235 Self::App(cmd) => {
236 write!(f, "app.{}", cmd)
237 }
238 Self::Plugin(pl_name, cmd) => {
239 write!(f, "plugin.{}.{}", pl_name, cmd)
240 }
241 Self::Aux(ns_name, cmd) => {
242 write!(f, "aux_ns.{}.{}", ns_name, cmd)
243 }
244 }
245 }
246}