1use std::time::Instant;
2
3use anyhow::{self, Context};
4use mini_async_repl::{
5 command::{
6 lift_validation_err, validate, Command, CommandArgInfo, CommandArgType, Critical,
7 ExecuteCommand,
8 },
9 CommandStatus, Repl,
10};
11use std::future::Future;
12use std::pin::Pin;
13
14struct OkCommandHandler {}
15impl OkCommandHandler {
16 pub fn new() -> Self {
17 Self {}
18 }
19 async fn handle_command(&mut self) -> anyhow::Result<CommandStatus> {
20 Ok(CommandStatus::Done)
21 }
22}
23impl ExecuteCommand for OkCommandHandler {
24 fn execute(
25 &mut self,
26 args: Vec<String>,
27 args_info: Vec<CommandArgInfo>,
28 ) -> Pin<Box<dyn Future<Output = anyhow::Result<CommandStatus>> + '_>> {
29 let valid = validate(args.clone(), args_info.clone());
30 if let Err(e) = valid {
31 return Box::pin(lift_validation_err(Err(e)));
32 }
33 Box::pin(self.handle_command())
34 }
35}
36
37struct RecoverableErrorHandler {}
38impl RecoverableErrorHandler {
39 pub fn new() -> Self {
40 Self {}
41 }
42 async fn handle_command(&mut self, text: String) -> anyhow::Result<CommandStatus> {
43 Self::may_throw(text)?;
44 Ok(CommandStatus::Done)
45 }
46 fn may_throw(description: String) -> Result<(), std::io::Error> {
49 Err(std::io::Error::new(std::io::ErrorKind::Other, description))
50 }
51}
52impl ExecuteCommand for RecoverableErrorHandler {
53 fn execute(
54 &mut self,
55 args: Vec<String>,
56 args_info: Vec<CommandArgInfo>,
57 ) -> Pin<Box<dyn Future<Output = anyhow::Result<CommandStatus>> + '_>> {
58 let valid = validate(args.clone(), args_info.clone());
59 if let Err(e) = valid {
60 return Box::pin(lift_validation_err(Err(e)));
61 }
62 Box::pin(self.handle_command(args[0].clone()))
63 }
64}
65
66struct CriticalErrorHandler {}
67impl CriticalErrorHandler {
68 pub fn new() -> Self {
69 Self {}
70 }
71 async fn handle_command(&mut self, text: String) -> anyhow::Result<CommandStatus> {
72 Self::may_throw(text).into_critical()?;
74 Ok(CommandStatus::Done)
75 }
76 fn may_throw(description: String) -> Result<(), std::io::Error> {
79 Err(std::io::Error::new(std::io::ErrorKind::Other, description))
80 }
81}
82impl ExecuteCommand for CriticalErrorHandler {
83 fn execute(
84 &mut self,
85 args: Vec<String>,
86 args_info: Vec<CommandArgInfo>,
87 ) -> Pin<Box<dyn Future<Output = anyhow::Result<CommandStatus>> + '_>> {
88 let valid = validate(args.clone(), args_info.clone());
89 if let Err(e) = valid {
90 return Box::pin(lift_validation_err(Err(e)));
91 }
92 Box::pin(self.handle_command(args[0].clone()))
93 }
94}
95
96struct RouletteErrorHandler {
97 start: Instant,
98}
99impl RouletteErrorHandler {
100 pub fn new(start: Instant) -> Self {
101 Self { start }
102 }
103 async fn handle_command(&mut self) -> anyhow::Result<CommandStatus> {
104 let ns = Instant::now().duration_since(self.start).as_nanos();
105 let cylinder = ns % 6;
106 match cylinder {
107 0 => Self::may_throw("Bang!".into()).into_critical()?,
108 1..=2 => Self::may_throw("Blank cartridge?".into())?,
109 _ => (),
110 }
111 Ok(CommandStatus::Done)
112 }
113 fn may_throw(description: String) -> Result<(), std::io::Error> {
116 Err(std::io::Error::new(std::io::ErrorKind::Other, description))
117 }
118}
119impl ExecuteCommand for RouletteErrorHandler {
120 fn execute(
121 &mut self,
122 args: Vec<String>,
123 args_info: Vec<CommandArgInfo>,
124 ) -> Pin<Box<dyn Future<Output = anyhow::Result<CommandStatus>> + '_>> {
125 let valid = validate(args.clone(), args_info.clone());
126 if let Err(e) = valid {
127 return Box::pin(lift_validation_err(Err(e)));
128 }
129 Box::pin(self.handle_command())
130 }
131}
132
133#[tokio::main]
134async fn main() -> anyhow::Result<()> {
135 #[rustfmt::skip]
136 let mut repl = Repl::builder()
137 .add("ok", Command::new(
138 "Run a command that just succeeds",
139 vec![],
140 Box::new(OkCommandHandler::new()),
141 ))
142 .add("error", Command::new(
143 "Command with recoverable error handled by the REPL",
144 vec![CommandArgInfo::new_with_name(CommandArgType::String, "text")],
145 Box::new(RecoverableErrorHandler::new()),
146 ))
147 .add("critical", Command::new(
148 "Command returns a critical error that must be handled outside of REPL",
149 vec![CommandArgInfo::new_with_name(CommandArgType::String, "text")],
150 Box::new(CriticalErrorHandler::new()),
151 ))
152 .add("roulette", Command::new(
153 "Feeling lucky?",
154 vec![],
155 Box::new(RouletteErrorHandler::new(Instant::now())),
156 ))
157 .build()
158 .context("Failed to create repl")?;
159
160 let repl_res = repl.run().await;
161 match repl_res {
162 Ok(_) => Ok(()),
163 Err(_) => {
164 println!("Repl halted. Quitting.");
165 Ok(())
166 }
167 }
168}