1- use crate :: state_machine:: { CharacterSet , StateMachine } ;
1+ use crate :: {
2+ api,
3+ state_machine:: { CharacterSet , StateMachine } ,
4+ } ;
25use reqwest:: blocking:: Client as HttpClient ;
36use serenity:: { model:: channel:: Message , prelude:: Context } ;
47use std:: { collections:: HashMap , sync:: Arc } ;
58
69const PREFIX : & ' static str = "?" ;
710pub ( crate ) type Result < T > = std:: result:: Result < T , Box < dyn std:: error:: Error > > ;
8- pub ( crate ) type CmdPtr = Arc < dyn for < ' m > Fn ( Args < ' m > ) -> Result < ( ) > + Send + Sync > ;
11+ pub ( crate ) type GuardFn = fn ( & Args ) -> Result < bool > ;
12+
13+ struct Command {
14+ guard : GuardFn ,
15+ ptr : Box < dyn for < ' m > Fn ( Args < ' m > ) -> Result < ( ) > + Send + Sync > ,
16+ }
17+
18+ impl Command {
19+ fn authorize ( & self , args : & Args ) -> Result < bool > {
20+ ( self . guard ) ( & args)
21+ }
22+
23+ fn call ( & self , args : Args ) -> Result < ( ) > {
24+ ( self . ptr ) ( args)
25+ }
26+ }
927
1028pub struct Args < ' m > {
1129 pub http : & ' m HttpClient ,
@@ -15,24 +33,33 @@ pub struct Args<'m> {
1533}
1634
1735pub ( crate ) struct Commands {
18- state_machine : StateMachine ,
36+ state_machine : StateMachine < Arc < Command > > ,
1937 client : HttpClient ,
20- menu : Option < String > ,
38+ menu : Option < HashMap < & ' static str , ( & ' static str , GuardFn ) > > ,
2139}
2240
2341impl Commands {
2442 pub ( crate ) fn new ( ) -> Self {
2543 Self {
2644 state_machine : StateMachine :: new ( ) ,
2745 client : HttpClient :: new ( ) ,
28- menu : Some ( String :: new ( ) ) ,
46+ menu : Some ( HashMap :: new ( ) ) ,
2947 }
3048 }
3149
3250 pub ( crate ) fn add (
3351 & mut self ,
3452 command : & ' static str ,
3553 handler : impl Fn ( Args ) -> Result < ( ) > + Send + Sync + ' static ,
54+ ) {
55+ self . add_protected ( command, handler, |_| Ok ( true ) ) ;
56+ }
57+
58+ pub ( crate ) fn add_protected (
59+ & mut self ,
60+ command : & ' static str ,
61+ handler : impl Fn ( Args ) -> Result < ( ) > + Send + Sync + ' static ,
62+ guard : GuardFn ,
3663 ) {
3764 info ! ( "Adding command {}" , & command) ;
3865 let mut state = 0 ;
@@ -89,7 +116,10 @@ impl Commands {
89116 }
90117 } ) ;
91118
92- let handler = Arc :: new ( handler) ;
119+ let handler = Arc :: new ( Command {
120+ guard,
121+ ptr : Box :: new ( handler) ,
122+ } ) ;
93123
94124 if opt_lambda_state. is_some ( ) {
95125 opt_final_states. iter ( ) . for_each ( |state| {
@@ -100,31 +130,76 @@ impl Commands {
100130 self . state_machine . set_final_state ( state) ;
101131 self . state_machine . set_handler ( state, handler. clone ( ) ) ;
102132 }
133+ }
134+
135+ pub ( crate ) fn help (
136+ & mut self ,
137+ cmd : & ' static str ,
138+ desc : & ' static str ,
139+ handler : impl Fn ( Args ) -> Result < ( ) > + Send + Sync + ' static ,
140+ ) {
141+ self . help_protected ( cmd, desc, handler, |_| Ok ( true ) ) ;
142+ }
143+
144+ pub ( crate ) fn help_protected (
145+ & mut self ,
146+ cmd : & ' static str ,
147+ desc : & ' static str ,
148+ handler : impl Fn ( Args ) -> Result < ( ) > + Send + Sync + ' static ,
149+ guard : GuardFn ,
150+ ) {
151+ let base_cmd = & cmd[ 1 ..] ;
152+ info ! ( "Adding command ?help {}" , & base_cmd) ;
153+ let mut state = 0 ;
103154
104155 self . menu . as_mut ( ) . map ( |menu| {
105- * menu += command ;
106- * menu += " \n "
156+ menu. insert ( cmd , ( desc , guard ) ) ;
157+ menu
107158 } ) ;
159+
160+ state = add_help_menu ( & mut self . state_machine , base_cmd, state) ;
161+ self . state_machine . set_final_state ( state) ;
162+ self . state_machine . set_handler (
163+ state,
164+ Arc :: new ( Command {
165+ guard,
166+ ptr : Box :: new ( handler) ,
167+ } ) ,
168+ ) ;
108169 }
109170
110- pub ( crate ) fn menu ( & mut self ) -> Option < String > {
111- self . menu . as_mut ( ) . map ( |menu| * menu += "?help\n " ) ;
171+ pub ( crate ) fn menu ( & mut self ) -> Option < HashMap < & ' static str , ( & ' static str , GuardFn ) > > {
112172 self . menu . take ( )
113173 }
114174
115175 pub ( crate ) fn execute < ' m > ( & ' m self , cx : Context , msg : Message ) {
116176 let message = & msg. content ;
117177 if !msg. is_own ( & cx) && message. starts_with ( PREFIX ) {
118178 self . state_machine . process ( message) . map ( |matched| {
119- info ! ( "Executing command {}" , message) ;
179+ info ! ( "Processing command: {}" , message) ;
120180 let args = Args {
121181 http : & self . client ,
122182 cx : & cx,
123183 msg : & msg,
124184 params : matched. params ,
125185 } ;
126- if let Err ( e) = ( matched. handler ) ( args) {
127- println ! ( "{}" , e) ;
186+ info ! ( "Checking permissions" ) ;
187+ match matched. handler . authorize ( & args) {
188+ Ok ( true ) => {
189+ info ! ( "Executing command" ) ;
190+ if let Err ( e) = matched. handler . call ( args) {
191+ error ! ( "{}" , e) ;
192+ }
193+ }
194+ Ok ( false ) => {
195+ info ! ( "Not executing command, unauthorized" ) ;
196+ if let Err ( e) =
197+ api:: send_reply ( & args, "You do not have permission to run this command" )
198+ {
199+ error ! ( "{}" , e) ;
200+ }
201+ }
202+ Err ( e) => error ! ( "{}" , e) ,
128203 }
129204 } ) ;
130205 }
@@ -145,7 +220,7 @@ fn key_value_pair(s: &'static str) -> Option<&'static str> {
145220 . flatten ( )
146221}
147222
148- fn add_space ( state_machine : & mut StateMachine , mut state : usize , i : usize ) -> usize {
223+ fn add_space < T > ( state_machine : & mut StateMachine < T > , mut state : usize , i : usize ) -> usize {
149224 if i > 0 {
150225 let mut char_set = CharacterSet :: from_char ( ' ' ) ;
151226 char_set. insert ( '\n' ) ;
@@ -156,8 +231,24 @@ fn add_space(state_machine: &mut StateMachine, mut state: usize, i: usize) -> us
156231 state
157232}
158233
159- fn add_dynamic_segment (
160- state_machine : & mut StateMachine ,
234+ fn add_help_menu < T > (
235+ mut state_machine : & mut StateMachine < T > ,
236+ cmd : & ' static str ,
237+ mut state : usize ,
238+ ) -> usize {
239+ "?help" . chars ( ) . for_each ( |ch| {
240+ state = state_machine. add ( state, CharacterSet :: from_char ( ch) ) ;
241+ } ) ;
242+ state = add_space ( & mut state_machine, state, 1 ) ;
243+ cmd. chars ( ) . for_each ( |ch| {
244+ state = state_machine. add ( state, CharacterSet :: from_char ( ch) ) ;
245+ } ) ;
246+
247+ state
248+ }
249+
250+ fn add_dynamic_segment < T > (
251+ state_machine : & mut StateMachine < T > ,
161252 name : & ' static str ,
162253 mut state : usize ,
163254) -> usize {
@@ -171,8 +262,8 @@ fn add_dynamic_segment(
171262 state
172263}
173264
174- fn add_remaining_segment (
175- state_machine : & mut StateMachine ,
265+ fn add_remaining_segment < T > (
266+ state_machine : & mut StateMachine < T > ,
176267 name : & ' static str ,
177268 mut state : usize ,
178269) -> usize {
@@ -185,8 +276,8 @@ fn add_remaining_segment(
185276 state
186277}
187278
188- fn add_code_segment_multi_line (
189- state_machine : & mut StateMachine ,
279+ fn add_code_segment_multi_line < T > (
280+ state_machine : & mut StateMachine < T > ,
190281 name : & ' static str ,
191282 mut state : usize ,
192283) -> usize {
@@ -219,8 +310,8 @@ fn add_code_segment_multi_line(
219310 state
220311}
221312
222- fn add_code_segment_single_line (
223- state_machine : & mut StateMachine ,
313+ fn add_code_segment_single_line < T > (
314+ state_machine : & mut StateMachine < T > ,
224315 name : & ' static str ,
225316 mut state : usize ,
226317 n_backticks : usize ,
@@ -239,7 +330,11 @@ fn add_code_segment_single_line(
239330 state
240331}
241332
242- fn add_key_value ( state_machine : & mut StateMachine , name : & ' static str , mut state : usize ) -> usize {
333+ fn add_key_value < T > (
334+ state_machine : & mut StateMachine < T > ,
335+ name : & ' static str ,
336+ mut state : usize ,
337+ ) -> usize {
243338 name. chars ( ) . for_each ( |c| {
244339 state = state_machine. add ( state, CharacterSet :: from_char ( c) ) ;
245340 } ) ;
0 commit comments