shellfish/handler/default.rs
1use std::collections::HashMap;
2
3use yansi::Paint;
4
5use crate::command::CommandType;
6use crate::Command;
7/// A handler lets you change how commands are run. They also let you
8/// change the shell built-ins. A handler takes a Vec<String> as
9/// input, and return a bool. A handler also takes a HashMap<String, Commands>,
10/// so it knows what commands it can run. Likewise, the state is also given.
11///
12/// The bool sent in return is wether or not this command should quit the
13/// shell. For example, in default shellfish, `true` is only every returned
14/// when the commands `quit` or `exit` are given.
15///
16/// For nearly every use case
17/// the default handler should be enough. If in doubt, you can create your
18/// own. I would recommend looking at shellfish's source code for an example
19pub trait Handler<T> {
20 fn handle(
21 &self,
22 args: Vec<String>,
23 commands: &HashMap<&str, Command<T>>,
24 state: &mut T,
25 description: &str,
26 ) -> bool;
27}
28
29/// Shellfish's default handler. This handler is pretty simple, given the
30/// only special options are `help`, `quit` and `exit`.
31#[derive(Default, Copy, Clone, Eq, PartialEq)]
32pub struct DefaultHandler();
33
34impl<T> Handler<T> for DefaultHandler {
35 fn handle(
36 &self,
37 line: Vec<String>,
38 commands: &HashMap<&str, Command<T>>,
39 state: &mut T,
40 description: &str,
41 ) -> bool {
42 if let Some(command) = line.first() {
43 // Add some padding.
44 println!();
45
46 match command.as_str() {
47 "quit" | "exit" => return true,
48 "help" => {
49 println!("{}", description);
50
51 // Print information about built-in commands
52 println!(" help - displays help information.");
53 println!(" quit - quits the shell.");
54 println!(" exit - exits the shell.");
55 for (name, command) in commands {
56 println!(" {} - {}", name, command.help);
57 }
58 }
59 _ => {
60 // Attempt to find the command
61 let command = commands.get(&*line[0]);
62
63 // Checks if we got it
64 match command {
65 Some(command) => {
66 if let Err(e) = match command.command {
67 CommandType::Sync(c) => c(state, line),
68 #[cfg(feature = "async")]
69 CommandType::Async(_) => {
70 eprintln!("{}", Paint::red("Async commands cannot be run in sync shells."));
71 Ok(())
72 }
73 } {
74 eprintln!("{}", Paint::red(&format!("Command exited unsuccessfully:\n{}\n({:?})", &e, &e)))
75 }
76 }
77 None => {
78 eprintln!(
79 "{} {}",
80 Paint::red("Command not found:"),
81 line[0]
82 )
83 }
84 }
85 }
86 }
87
88 // Padding
89 println!();
90 }
91 false
92 }
93}