@@ -11,8 +11,13 @@ pub mod prioritize;
11
11
pub mod relabel;
12
12
pub mod second;
13
13
14
- pub fn find_command_start ( input : & str , bot : & str ) -> Option < usize > {
15
- input. to_ascii_lowercase ( ) . find ( & format ! ( "@{}" , bot) )
14
+ pub fn find_command_start ( input : & str , bots : & [ & str ] ) -> Option < usize > {
15
+ let input = input. to_ascii_lowercase ( ) ;
16
+ bots. iter ( )
17
+ . map ( |name| format ! ( "@{}" , name) )
18
+ . chain ( std:: iter:: once ( "r?" . to_owned ( ) ) )
19
+ . filter_map ( |s| input. find ( & s) )
20
+ . min ( )
16
21
}
17
22
18
23
#[ derive( Debug , PartialEq ) ]
@@ -69,61 +74,98 @@ impl<'a> Input<'a> {
69
74
70
75
fn parse_command ( & mut self ) -> Option < Command < ' a > > {
71
76
let mut tok = Tokenizer :: new ( & self . all [ self . parsed ..] ) ;
72
- let name_length = if let Ok ( Some ( Token :: Word ( bot_name) ) ) = tok. next_token ( ) {
73
- assert ! ( self
74
- . bot
75
- . iter( )
76
- . any( |name| bot_name. eq_ignore_ascii_case( & format!( "@{}" , name) ) ) ) ;
77
- bot_name. len ( )
78
- } else {
79
- panic ! ( "no bot name?" )
77
+ let review = match tok. next_token ( ) {
78
+ Ok ( Some ( Token :: Word ( r) ) ) if r == "r" || r == "R" => true ,
79
+ Ok ( Some ( Token :: Word ( bot_name) ) ) => {
80
+ assert ! (
81
+ self . bot
82
+ . iter( )
83
+ . any( |name| bot_name. eq_ignore_ascii_case( & format!( "@{}" , name) ) ) ,
84
+ "not this bot name? ({:?})" ,
85
+ bot_name
86
+ ) ;
87
+ false
88
+ }
89
+ other => panic ! ( "neiter review nor bot name? ({:?})" , other) ,
80
90
} ;
81
- log:: info!( "identified potential command" ) ;
82
91
83
92
let mut success = vec ! [ ] ;
84
93
85
- let original_tokenizer = tok. clone ( ) ;
94
+ if review {
95
+ match tok. next_token ( ) {
96
+ Ok ( Some ( Token :: Question ) ) => { }
97
+ other => {
98
+ log:: trace!( "received odd review start token: {:?}" , other) ;
99
+ return None ;
100
+ }
101
+ }
102
+ log:: info!( "identified potential review request" ) ;
103
+ match tok. next_token ( ) {
104
+ Ok ( Some ( Token :: Word ( w) ) ) => {
105
+ let mentions = crate :: mentions:: get_mentions ( w) ;
106
+ if let [ a] = & mentions[ ..] {
107
+ success. push ( (
108
+ tok,
109
+ Command :: Assign ( Ok ( assign:: AssignCommand :: User {
110
+ username : ( * a) . to_owned ( ) ,
111
+ } ) ) ,
112
+ ) ) ;
113
+ } else {
114
+ log:: trace!( "{:?} had non-one mention: {:?}" , w, mentions) ;
115
+ return None ;
116
+ }
117
+ }
118
+ other => {
119
+ log:: trace!( "received odd review start token: {:?}" , other) ;
120
+ return None ;
121
+ }
122
+ }
123
+ } else {
124
+ log:: info!( "identified potential command" ) ;
86
125
87
- success. extend ( parse_single_command (
88
- relabel:: RelabelCommand :: parse,
89
- Command :: Relabel ,
90
- & original_tokenizer,
91
- ) ) ;
92
- success. extend ( parse_single_command (
93
- assign:: AssignCommand :: parse,
94
- Command :: Assign ,
95
- & original_tokenizer,
96
- ) ) ;
97
- success. extend ( parse_single_command (
98
- ping:: PingCommand :: parse,
99
- Command :: Ping ,
100
- & original_tokenizer,
101
- ) ) ;
102
- success. extend ( parse_single_command (
103
- nominate:: NominateCommand :: parse,
104
- Command :: Nominate ,
105
- & original_tokenizer,
106
- ) ) ;
107
- success. extend ( parse_single_command (
108
- prioritize:: PrioritizeCommand :: parse,
109
- Command :: Prioritize ,
110
- & original_tokenizer,
111
- ) ) ;
112
- success. extend ( parse_single_command (
113
- second:: SecondCommand :: parse,
114
- Command :: Second ,
115
- & original_tokenizer,
116
- ) ) ;
117
- success. extend ( parse_single_command (
118
- glacier:: GlacierCommand :: parse,
119
- Command :: Glacier ,
120
- & original_tokenizer,
121
- ) ) ;
122
- success. extend ( parse_single_command (
123
- close:: CloseCommand :: parse,
124
- Command :: Close ,
125
- & original_tokenizer,
126
- ) ) ;
126
+ let original_tokenizer = tok. clone ( ) ;
127
+
128
+ success. extend ( parse_single_command (
129
+ relabel:: RelabelCommand :: parse,
130
+ Command :: Relabel ,
131
+ & original_tokenizer,
132
+ ) ) ;
133
+ success. extend ( parse_single_command (
134
+ assign:: AssignCommand :: parse,
135
+ Command :: Assign ,
136
+ & original_tokenizer,
137
+ ) ) ;
138
+ success. extend ( parse_single_command (
139
+ ping:: PingCommand :: parse,
140
+ Command :: Ping ,
141
+ & original_tokenizer,
142
+ ) ) ;
143
+ success. extend ( parse_single_command (
144
+ nominate:: NominateCommand :: parse,
145
+ Command :: Nominate ,
146
+ & original_tokenizer,
147
+ ) ) ;
148
+ success. extend ( parse_single_command (
149
+ prioritize:: PrioritizeCommand :: parse,
150
+ Command :: Prioritize ,
151
+ & original_tokenizer,
152
+ ) ) ;
153
+ success. extend ( parse_single_command (
154
+ second:: SecondCommand :: parse,
155
+ Command :: Second ,
156
+ & original_tokenizer,
157
+ ) ) ;
158
+ success. extend ( parse_single_command (
159
+ glacier:: GlacierCommand :: parse,
160
+ Command :: Glacier ,
161
+ & original_tokenizer,
162
+ ) ) ;
163
+ success. extend ( parse_single_command (
164
+ close:: CloseCommand :: parse,
165
+ Command :: Close ,
166
+ & original_tokenizer,
167
+ ) ) ;
168
+ }
127
169
128
170
if success. len ( ) > 1 {
129
171
panic ! (
@@ -133,6 +175,8 @@ impl<'a> Input<'a> {
133
175
) ;
134
176
}
135
177
178
+ let ( mut tok, c) = success. pop ( ) ?;
179
+
136
180
if self
137
181
. code
138
182
. overlaps_code ( ( self . parsed ) ..( self . parsed + tok. position ( ) ) )
@@ -142,13 +186,8 @@ impl<'a> Input<'a> {
142
186
return None ;
143
187
}
144
188
145
- let ( mut tok, c) = success. pop ( ) ?;
146
189
// if we errored out while parsing the command do not move the input forwards
147
- self . parsed += if c. is_ok ( ) {
148
- tok. position ( )
149
- } else {
150
- name_length
151
- } ;
190
+ self . parsed += if c. is_ok ( ) { tok. position ( ) } else { 1 } ;
152
191
Some ( c)
153
192
}
154
193
}
@@ -158,16 +197,12 @@ impl<'a> Iterator for Input<'a> {
158
197
159
198
fn next ( & mut self ) -> Option < Command < ' a > > {
160
199
loop {
161
- let start = self
162
- . bot
163
- . iter ( )
164
- . filter_map ( |name| find_command_start ( & self . all [ self . parsed ..] , name) )
165
- . min ( ) ?;
200
+ let start = find_command_start ( & self . all [ self . parsed ..] , & self . bot ) ?;
166
201
self . parsed += start;
167
202
if let Some ( command) = self . parse_command ( ) {
168
203
return Some ( command) ;
169
204
}
170
- self . parsed += self . bot . len ( ) + 1 ;
205
+ self . parsed += 1 ;
171
206
}
172
207
}
173
208
}
@@ -247,7 +282,7 @@ fn move_input_along_1() {
247
282
let mut input = Input :: new ( input, vec ! [ "bot" ] ) ;
248
283
assert ! ( input. next( ) . unwrap( ) . is_err( ) ) ;
249
284
// don't move input along if parsing the command fails
250
- assert_eq ! ( & input. all[ ..input. parsed] , "@bot " ) ;
285
+ assert_eq ! ( & input. all[ ..input. parsed] , "@" ) ;
251
286
}
252
287
253
288
#[ test]
@@ -262,3 +297,25 @@ fn multiname() {
262
297
assert ! ( input. next( ) . unwrap( ) . is_ok( ) ) ;
263
298
assert ! ( input. next( ) . is_none( ) ) ;
264
299
}
300
+
301
+ #[ test]
302
+ fn parse_assign_review ( ) {
303
+ let input = "R? @user" ;
304
+ let mut input = Input :: new ( input, vec ! [ "bot" ] ) ;
305
+ match input. next ( ) . unwrap ( ) {
306
+ Command :: Assign ( Ok ( x) ) => assert_eq ! (
307
+ x,
308
+ assign:: AssignCommand :: User {
309
+ username: String :: from( "user" ) ,
310
+ }
311
+ ) ,
312
+ o => panic ! ( "unknown: {:?}" , o) ,
313
+ } ;
314
+ }
315
+
316
+ #[ test]
317
+ fn parse_assign_review_no_panic ( ) {
318
+ let input = "R ?" ;
319
+ let mut input = Input :: new ( input, vec ! [ "bot" ] ) ;
320
+ assert ! ( input. next( ) . is_none( ) ) ;
321
+ }
0 commit comments