climb/command.rs
1use crate::types::*;
2
3/// Holds information about command options.
4///
5/// `CommandOption`s are the options that can be passed in when calling
6/// application commands from the command line. They can also accept an
7/// argument.
8///
9/// They can be added to commands using the [Command] API.
10///
11/// # Examples
12///
13/// Creating a command option and assigning it an alias and argument:
14///
15/// ```
16/// use climb::CommandOption;
17///
18/// let my_option = CommandOption::new(
19/// "delete-folder",
20/// "This option tells the command you want to delete a folder"
21/// )
22/// .alias("d")
23/// .arg("folder_name");
24/// ```
25///
26/// This option can be used with the command like this:
27///
28/// `[COMMAND] --delete-folder <folder_name> ...`
29///
30/// or
31///
32/// `[COMMAND] -d <folder_name> ...`
33pub struct CommandOption {
34 pub(crate) alias_long: String,
35 pub(crate) alias_short: Option<String>,
36 pub(crate) arg: Option<String>,
37 pub(crate) description: String,
38}
39
40impl CommandOption {
41 /// Construct and returns a default CommandOption.
42 ///
43 /// Creates the command option and initializes it with the given alias
44 /// and description. The provided alias is the long alias. It will be
45 /// prepended with `--` automatically when the application is constructed.
46 ///
47 /// # Arguments
48 ///
49 /// * `alias_long` - The long alias of the option (more than one character)
50 /// * `desc` - The description of the command
51 ///
52 /// # Examples
53 ///
54 /// Creating a new command. This command will be called using the
55 /// `--recursive` option in the command line:
56 ///
57 /// ```
58 /// use climb::CommandOption;
59 ///
60 /// let my_option = CommandOption::new(
61 /// "recursive",
62 /// "This option lets the command behave recursively"
63 /// );
64 /// ```
65 pub fn new(alias_long: &str, desc: &str) -> Self {
66 if alias_long.len() <= 1 {
67 panic!(
68 "Long option aliases should have more than a single character: `{}`",
69 alias_long
70 );
71 }
72
73 let mut alias = String::from(alias_long);
74 alias.insert_str(0, "--");
75 CommandOption {
76 alias_long: alias,
77 alias_short: None,
78 arg: None,
79 description: String::from(desc),
80 }
81 }
82
83 /// Assign a short alias to the option
84 ///
85 /// The short alias is a single character alias prepended by
86 /// a single dash `-`. For example, `--help` can also be called with
87 /// `-h`. Options do not have a short alias by default.
88 ///
89 /// # Arguments
90 /// * `alias` - A string slice holding the short alias of the option.
91 /// Must be a single character.
92 ///
93 /// # Examples
94 ///
95 /// Creating an option and adding a short alias. This option can
96 /// be called using `--recursive` or `-r`:
97 ///
98 /// ```
99 /// use climb::CommandOption;
100 ///
101 /// let my_option = CommandOption::new(
102 /// "recursive",
103 /// "This option lets the command behave recursively"
104 /// )
105 /// .alias("r");
106 /// ```
107 pub fn alias(mut self, alias: &str) -> Self {
108 if alias.len() != 1 {
109 panic!(
110 "Short option aliases can only be a single character: `{}`",
111 alias
112 );
113 }
114
115 let mut alias = String::from(alias);
116 alias.insert(0, '-');
117 self.alias_short = Some(alias);
118 self
119 }
120
121 /// Assign an argument to the option
122 ///
123 /// Option arguments are passed in after the option is provided
124 /// in the command line arguments.
125 ///
126 /// # Arguments:
127 /// * `argument_name` - The name of the argument
128 ///
129 /// # Examples
130 ///
131 /// Creating an option and adding an argument named `folder_name`.
132 /// This option argument can be used like this:
133 ///
134 /// \[command_name\] --delete-folder <folder_name> ...
135 ///
136 /// ```
137 /// use climb::CommandOption;
138 ///
139 /// let my_option = CommandOption::new(
140 /// "delete-folder",
141 /// "This tells the command you want to delete a folder"
142 /// )
143 /// .arg("folder_name");
144 /// ```
145 pub fn arg(mut self, argument_name: &str) -> Self {
146 self.arg = Some(argument_name.to_uppercase());
147 self
148 }
149}
150
151/// Holds information about the commands the application can call.
152///
153/// A command stores an alias to call it by, valid options, arguments, and
154/// a function that is called whenever the command is executed. This function
155/// is the code that you should write to perform the logic of the command.
156///
157/// When you call the command from the command line, Climb will parse the arguments
158/// and pass them into your function. See more about this in the [CommandFunction]
159/// documentation.
160///
161/// # Examples
162///
163/// Creating a command, changing some of its values, and adding it to
164/// an application:
165///
166/// ```
167/// use climb::*;
168///
169/// fn example_cmd_fn(_: FunctionInput, _: FunctionOptions) -> FunctionResult {
170/// println!("my example function");
171/// Ok(None)
172/// }
173///
174/// let my_command = Command::new(
175/// "cmd_name",
176/// "cmd_desc",
177/// example_cmd_fn
178/// )
179/// .alias("c")
180/// .option(CommandOption::new(
181/// "option_name",
182/// "option_desc"
183/// ))
184/// .arg("arg1")
185/// .arg("arg2");
186///
187/// let my_app = create_app!()
188/// .command(my_command);
189/// ```
190pub struct Command {
191 pub(crate) function: CommandFunction,
192 pub(crate) alias_long: String,
193 pub(crate) alias_short: Option<String>,
194 pub(crate) options: Vec<CommandOption>,
195 pub(crate) args: Vec<String>,
196 pub(crate) desc: String,
197}
198
199impl Command {
200 /// Construct and return a command with the given alias, desc,
201 /// and function.
202 ///
203 /// # Arguments
204 /// * `alias` - String slice that holds the alias used to call the
205 /// command from the terminal
206 /// * `desc` - String slice that holds the command description
207 /// * `function` - Function that matches the [CommandFunction] signature
208 ///
209 /// # Examples
210 ///
211 /// Construct a new command:
212 ///
213 /// ```
214 /// use climb::*;
215 ///
216 /// fn example_cmd_fn(_: FunctionInput, _: FunctionOptions) -> FunctionResult {
217 /// println!("my example function");
218 /// Ok(None)
219 /// }
220 ///
221 /// let my_command = Command::new(
222 /// "cmd_name",
223 /// "cmd_desc",
224 /// example_cmd_fn
225 /// );
226 /// ```
227 pub fn new(alias: &str, desc: &str, function: CommandFunction) -> Self {
228 if alias.len() <= 1 {
229 panic!(
230 "Long command aliases should have more than a single character: `{}`",
231 alias
232 );
233 }
234
235 // Construct a default command with only the help menu option
236 Command {
237 function,
238 alias_long: alias.to_lowercase(),
239 alias_short: None,
240 options: vec![CommandOption::new("help", "Print help information").alias("h")],
241 args: vec![],
242 desc: String::from(desc),
243 }
244 }
245
246 /// Assign an short alias to the command (single character).
247 ///
248 /// The command can be called using either the normal long alias or this
249 /// shorter one.
250 ///
251 /// # Arguments
252 /// * `alias` - String slice holding the short alias (must be a single
253 /// character)
254 ///
255 /// # Examples
256 ///
257 /// Construct a new command and assign a new alias `c`:
258 ///
259 /// ```
260 /// use climb::*;
261 ///
262 /// fn example_cmd_fn(_: FunctionInput, _: FunctionOptions) -> FunctionResult {
263 /// println!("my example function");
264 /// Ok(None)
265 /// }
266 ///
267 /// let my_command = Command::new(
268 /// "cmd_name",
269 /// "cmd_desc",
270 /// example_cmd_fn
271 /// )
272 /// .alias("c");
273 /// ```
274 ///
275 /// The command can now be called using either `cmd_name` or `c`.
276 pub fn alias(mut self, alias: &str) -> Self {
277 if alias.len() != 1 {
278 panic!(
279 "Short command aliases can only be a single character: `{}`",
280 alias
281 );
282 }
283
284 self.alias_short = Some(alias.to_lowercase());
285 self
286 }
287
288 /// Add an option to the command.
289 ///
290 /// You can add as many options as you like to a command. For more
291 /// about options, see [CommandOption].
292 ///
293 /// # Arguments
294 /// * `option` - A `CommandOption` struct holding the option to be added
295 ///
296 /// # Examples
297 ///
298 /// Construct a command and add an option:
299 ///
300 /// ```
301 /// use climb::*;
302 ///
303 /// fn example_cmd_fn(_: FunctionInput, _: FunctionOptions) -> FunctionResult {
304 /// println!("my example function");
305 /// Ok(None)
306 /// }
307 ///
308 /// let my_command = Command::new(
309 /// "cmd_name",
310 /// "cmd_desc",
311 /// example_cmd_fn
312 /// )
313 /// .option(CommandOption::new(
314 /// "option_name",
315 /// "option_desc"
316 /// ));
317 /// ```
318 ///
319 /// The option can now be called using `[cmd_name] --option_name ...`
320 pub fn option(mut self, option: CommandOption) -> Self {
321 self.options.push(option);
322 self
323 }
324
325 /// Add an argument to the command.
326 ///
327 /// Command arguments allow you to pass data into the command from the
328 /// command line. For example, if you wanted a command to perform actions on
329 /// a file, you could pass in the file name as an argument.
330 ///
331 /// # Arguments
332 /// * `name` - String slice that holds the name of the argument
333 ///
334 /// # Examples
335 ///
336 /// Construct a new command and add an argument to it:
337 ///
338 /// ```
339 /// use climb::*;
340 ///
341 /// fn example_cmd_fn(_: FunctionInput, _: FunctionOptions) -> FunctionResult {
342 /// println!("my example function");
343 /// Ok(None)
344 /// }
345 ///
346 /// let my_command = Command::new(
347 /// "cmd_name",
348 /// "cmd_desc",
349 /// example_cmd_fn
350 /// )
351 /// .arg("arg1");
352 /// ```
353 ///
354 /// The command can be called using:
355 ///
356 /// `[app_name] cmd_name <arg1> ...`
357 pub fn arg(mut self, name: &str) -> Self {
358 self.args.push(name.to_uppercase());
359 self
360 }
361
362 // If the command has the option, return a reference to it
363 pub(crate) fn has_option(&self, alias: &String) -> Option<&CommandOption> {
364 for option in &self.options {
365 let equals_alias_short = if let Some(alias_short) = &option.alias_short {
366 *alias == *alias_short
367 } else {
368 false
369 };
370
371 if option.alias_long == *alias || equals_alias_short {
372 return Some(option);
373 }
374 }
375
376 None
377 }
378}