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