1use crate::input_parser::InputParser;
2use crate::input_reader::{InputReader, InputResult};
3
4pub type ShellResult = anyhow::Result<ShellAction>;
5
6pub enum ShellAction {
7 Continue,
8 UpdatePrompt(String),
9 Exit,
10}
11
12pub trait Handler<A, T> {
13 fn handle(&self, args: A, context: &mut T) -> ShellResult;
14}
15
16impl<A, T, F> Handler<A, T> for F
21where
22 F: Fn(A, &mut T) -> ShellResult,
23{
24 fn handle(&self, args: A, context: &mut T) -> ShellResult {
25 self(args, context)
26 }
27}
28
29pub struct Shell<T, I: InputReader, P: InputParser<T>, H: Handler<P::Output, T>> {
30 prompt: String,
31 context: T,
32 input_reader: I,
33 input_parser: P,
34 handler: H,
35}
36
37impl<T, I: InputReader, P: InputParser<T>, H: Handler<P::Output, T>> Shell<T, I, P, H> {
38 pub fn new(
39 prompt: impl std::fmt::Display,
40 context: T,
41 input_reader: I,
42 input_parser: P,
43 handler: H,
44 ) -> Self {
45 Self {
46 prompt: prompt.to_string(),
47 context,
48 input_reader,
49 input_parser,
50 handler,
51 }
52 }
53
54 pub fn run(&mut self) -> anyhow::Result<()> {
55 loop {
56 let input_res = self.input_reader.read(&self.prompt)?;
57 match input_res {
58 InputResult::Input(input) => {
59 let res = self.handle_input(&input);
60 match res {
61 Ok(shell_res) => match shell_res {
62 Ok(ShellAction::Continue) => continue,
63 Ok(ShellAction::UpdatePrompt(prompt)) => {
64 self.prompt = prompt;
65 continue;
66 }
67 Ok(ShellAction::Exit) => break,
68 Err(e) => println!("{}", e),
69 },
70 Err(e) => println!("{}", e),
71 }
72 }
73 InputResult::Interrupted => continue,
74 InputResult::Eof => break,
75 }
76 }
77 Ok(())
78 }
79
80 fn handle_input(&mut self, input: &str) -> Result<ShellResult, String> {
81 let args = self
84 .input_parser
85 .parse(input, &mut self.context)
86 .map_err(|e| e.to_string())?;
87
88 let res = self.handler.handle(args, &mut self.context);
89
90 Ok(res)
91 }
92}