88//! Command: `@bot merge`, `@bot hold`, `@bot restart`, `@bot dissent`, `@bot stabilize` or `@bot close`.
99//! ```
1010
11+ use std:: fmt;
12+
1113use crate :: error:: Error ;
1214use crate :: token:: { Token , Tokenizer } ;
1315use postgres_types:: { FromSql , ToSql } ;
@@ -23,20 +25,49 @@ pub struct DecisionCommand {
2325
2426impl DecisionCommand {
2527 pub fn parse < ' a > ( input : & mut Tokenizer < ' a > ) -> Result < Option < Self > , Error < ' a > > {
26- if let Some ( Token :: Word ( "merge" ) ) = input. peek_token ( ) ? {
27- Ok ( Some ( Self {
28- resolution : Resolution :: Merge ,
29- reversibility : Reversibility :: Reversible ,
30- } ) )
31- } else {
32- Ok ( None )
28+ let mut toks = input. clone ( ) ;
29+
30+ match toks. peek_token ( ) ? {
31+ Some ( Token :: Word ( "merge" ) ) => {
32+ command_or_error ( input, & mut toks, Self {
33+ resolution : Resolution :: Merge ,
34+ reversibility : Reversibility :: Reversible ,
35+ } )
36+ }
37+ Some ( Token :: Word ( "hold" ) ) => {
38+ command_or_error ( input, & mut toks, Self {
39+ resolution : Resolution :: Hold ,
40+ reversibility : Reversibility :: Reversible ,
41+ } )
42+ }
43+ _ => Ok ( None ) ,
3344 }
3445 }
3546}
3647
37- #[ derive( Debug , Eq , PartialEq ) ]
48+ fn command_or_error < ' a > ( input : & mut Tokenizer < ' a > , toks : & mut Tokenizer < ' a > , command : DecisionCommand ) -> Result < Option < DecisionCommand > , Error < ' a > > {
49+ toks. next_token ( ) ?;
50+ if let Some ( Token :: Dot ) | Some ( Token :: EndOfLine ) = toks. peek_token ( ) ? {
51+ * input = toks. clone ( ) ;
52+ Ok ( Some ( command) )
53+ } else {
54+ Err ( toks. error ( ParseError :: ExpectedEnd ) )
55+ }
56+ }
57+
58+ #[ derive( PartialEq , Eq , Debug ) ]
3859pub enum ParseError {
39- InvalidFirstCommand ,
60+ ExpectedEnd ,
61+ }
62+
63+ impl std:: error:: Error for ParseError { }
64+
65+ impl fmt:: Display for ParseError {
66+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
67+ match self {
68+ ParseError :: ExpectedEnd => write ! ( f, "expected end of command" ) ,
69+ }
70+ }
4071}
4172
4273#[ derive( Serialize , Deserialize , Debug , Clone , ToSql , FromSql , Eq , PartialEq ) ]
@@ -56,3 +87,58 @@ pub enum Resolution {
5687 #[ postgres( name = "hold" ) ]
5788 Hold ,
5889}
90+ #[ cfg( test) ]
91+ mod tests {
92+ use super :: * ;
93+
94+ fn parse < ' a > ( input : & ' a str ) -> Result < Option < DecisionCommand > , Error < ' a > > {
95+ let mut toks = Tokenizer :: new ( input) ;
96+ Ok ( DecisionCommand :: parse ( & mut toks) ?)
97+ }
98+
99+ #[ test]
100+ fn test_correct_merge ( ) {
101+ assert_eq ! (
102+ parse( "merge" ) ,
103+ Ok ( Some ( DecisionCommand {
104+ resolution: Resolution :: Merge ,
105+ reversibility: Reversibility :: Reversible
106+ } ) ) ,
107+ ) ;
108+ }
109+
110+ #[ test]
111+ fn test_correct_merge_final_dot ( ) {
112+ assert_eq ! (
113+ parse( "merge." ) ,
114+ Ok ( Some ( DecisionCommand {
115+ resolution: Resolution :: Merge ,
116+ reversibility: Reversibility :: Reversible
117+ } ) ) ,
118+ ) ;
119+ }
120+
121+ #[ test]
122+ fn test_correct_hold ( ) {
123+ assert_eq ! (
124+ parse( "hold" ) ,
125+ Ok ( Some ( DecisionCommand {
126+ resolution: Resolution :: Hold ,
127+ reversibility: Reversibility :: Reversible
128+ } ) ) ,
129+ ) ;
130+ }
131+
132+ #[ test]
133+ fn test_expected_end ( ) {
134+ use std:: error:: Error ;
135+ assert_eq ! (
136+ parse( "hold my beer" )
137+ . unwrap_err( )
138+ . source( )
139+ . unwrap( )
140+ . downcast_ref( ) ,
141+ Some ( & ParseError :: ExpectedEnd ) ,
142+ ) ;
143+ }
144+ }
0 commit comments