@@ -4,84 +4,232 @@ use std::collections::BTreeMap;
4
4
5
5
use diesel:: prelude:: * ;
6
6
use toml;
7
+ use serde:: de;
7
8
8
9
use super :: DB_POOL ;
9
10
use domain:: github:: GitHubUser ;
10
11
use error:: * ;
11
12
13
+ //==============================================================================
14
+ // Public API
15
+ //==============================================================================
16
+
12
17
lazy_static ! {
13
- pub static ref TEAMS : Teams = {
14
- let teams_file =
15
- include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ) , "/teams.toml" ) ) ;
16
- let teams_from_file: TeamsFromFile =
17
- toml:: from_str( teams_file) . expect( "couldn't parse teams" ) ;
18
-
19
- let mut teams = BTreeMap :: new( ) ;
20
-
21
- for ( label, team_from_file) in teams_from_file {
22
- let label = TeamLabel ( label) ;
23
- let team = team_from_file. validate( )
24
- . expect( "unable to verify team member from database.
25
- if you're running this for tests, make sure you've pulled github users from prod" ) ;
26
- teams. insert( label, team) ;
27
- }
18
+ pub static ref SETUP : RfcbotConfig = read_rfcbot_cfg_validated( ) ;
19
+ }
20
+
21
+ #[ derive( Debug , Deserialize ) ]
22
+ pub struct RfcbotConfig {
23
+ fcp_behaviors : BTreeMap < String , FcpBehavior > ,
24
+ teams : BTreeMap < TeamLabel , Team > ,
25
+ }
26
+
27
+ impl RfcbotConfig {
28
+ /// Retrive an iterator over all the team labels.
29
+ pub fn team_labels ( & self ) -> impl Iterator < Item = & TeamLabel > {
30
+ self . teams . keys ( )
31
+ }
32
+
33
+ /// Retrive an iterator over all the (team label, team) pairs.
34
+ pub fn teams ( & self ) -> impl Iterator < Item = ( & TeamLabel , & Team ) > {
35
+ self . teams . iter ( )
36
+ }
37
+
38
+ /// Are we allowed to auto-close issues after F-FCP in this repo?
39
+ pub fn ffcp_auto_close ( & self , repo : & str ) -> bool {
40
+ self . fcp_behaviors . get ( repo) . map ( |fcp| fcp. close ) . unwrap_or_default ( )
41
+ }
28
42
29
- teams
30
- } ;
43
+ /// Are we allowed to auto-postpone issues after F-FCP in this repo?
44
+ pub fn ffcp_auto_postpone ( & self , repo : & str ) -> bool {
45
+ self . fcp_behaviors . get ( repo) . map ( |fcp| fcp. postpone ) . unwrap_or_default ( )
46
+ }
47
+ }
48
+
49
+ #[ derive( Debug , Deserialize ) ]
50
+ pub struct FcpBehavior {
51
+ #[ serde( default ) ]
52
+ pub close : bool ,
53
+ #[ serde( default ) ]
54
+ pub postpone : bool ,
55
+ }
56
+
57
+ #[ derive( Debug , Deserialize ) ]
58
+ pub struct Team {
59
+ // FIXME: The two following first fields don't seem to be used anywhere...
60
+ // If this is intended, document why.
61
+ name : String ,
62
+ ping : String ,
63
+ members : Vec < String > ,
64
+ }
65
+
66
+ impl Team {
67
+ pub fn member_logins ( & self ) -> impl Iterator < Item = & str > {
68
+ self . members . iter ( ) . map ( |s| s. as_str ( ) )
69
+ }
31
70
}
32
71
33
72
#[ derive( Debug , Eq , Hash , Ord , PartialEq , PartialOrd ) ]
34
73
pub struct TeamLabel ( pub String ) ;
35
74
36
- type TeamsFromFile = BTreeMap < String , TeamFromFile > ;
37
- pub type Teams = BTreeMap < TeamLabel , Team > ;
75
+ impl < ' de > de:: Deserialize < ' de > for TeamLabel {
76
+ fn deserialize < D : de:: Deserializer < ' de > > ( de : D ) -> Result < Self , D :: Error > {
77
+ String :: deserialize ( de) . map ( TeamLabel )
78
+ }
38
79
39
- #[ derive( Debug , Deserialize ) ]
40
- struct TeamFromFile {
41
- name : String ,
42
- ping : String ,
43
- members : Vec < String > ,
80
+ fn deserialize_in_place < D : de:: Deserializer < ' de > > ( de : D , place : & mut Self )
81
+ -> Result < ( ) , D :: Error >
82
+ {
83
+ String :: deserialize_in_place ( de, & mut place. 0 )
84
+ }
85
+ }
86
+
87
+ //==============================================================================
88
+ // Implementation details
89
+ //==============================================================================
90
+
91
+ /// Read the validated `rfcbot.toml` configuration file.
92
+ fn read_rfcbot_cfg_validated ( ) -> RfcbotConfig {
93
+ let cfg = read_rfcbot_cfg ( ) ;
94
+
95
+ cfg. teams . values ( ) . for_each ( |team|
96
+ team. validate ( )
97
+ . expect ( "unable to verify team member from database.
98
+ if you're running this for tests, make sure you've pulled github users from prod" )
99
+ ) ;
100
+
101
+ cfg
102
+ }
103
+
104
+ /// Read the unprocessed `rfcbot.toml` configuration file.
105
+ fn read_rfcbot_cfg ( ) -> RfcbotConfig {
106
+ read_rfcbot_cfg_from (
107
+ include_str ! ( concat!( env!( "CARGO_MANIFEST_DIR" ) , "/rfcbot.toml" ) ) )
108
+ }
109
+
110
+ fn read_rfcbot_cfg_from ( input : & str ) -> RfcbotConfig {
111
+ toml:: from_str ( input) . expect ( "couldn't parse rfcbot.toml!" )
44
112
}
45
113
46
- impl TeamFromFile {
47
- pub fn validate ( self ) -> DashResult < Team > {
48
- use domain:: schema:: githubuser:: dsl:: * ;
49
- let conn = & * ( DB_POOL . get ( ) ?) ;
50
-
51
- // bail if they don't exist, but we don't want to actually keep the id in ram
52
- for member_login in & self . members {
53
- match githubuser
54
- . filter ( login. eq ( member_login) )
55
- . first :: < GitHubUser > ( conn) {
56
- Ok ( _) => ( ) ,
57
- Err ( why) => {
58
- error ! ( "unable to find {} in database: {:?}" , member_login, why) ;
59
- return Err ( why. into ( ) ) ;
114
+ impl Team {
115
+ fn validate ( & self ) -> DashResult < ( ) > {
116
+ use domain:: schema:: githubuser:: dsl:: * ;
117
+ let conn = & * ( DB_POOL . get ( ) ?) ;
118
+
119
+ // bail if they don't exist, but we don't want to actually keep the id in ram
120
+ for member_login in self . member_logins ( ) {
121
+ match githubuser
122
+ . filter ( login. eq ( member_login) )
123
+ . first :: < GitHubUser > ( conn)
124
+ {
125
+ Ok ( _) => ( ) ,
126
+ Err ( why) => {
127
+ error ! ( "unable to find {} in database: {:?}" , member_login, why) ;
128
+ return Err ( why. into ( ) ) ;
129
+ }
60
130
}
61
131
}
62
- }
63
132
64
- Ok ( Team {
65
- name : self . name ,
66
- ping : self . ping ,
67
- member_logins : self . members ,
68
- } )
69
- }
133
+ Ok ( ( ) )
134
+ }
70
135
}
71
136
72
- pub struct Team {
73
- pub name : String ,
74
- pub ping : String ,
75
- pub member_logins : Vec < String > ,
76
- }
137
+ //==============================================================================
138
+ // Tests
139
+ //==============================================================================
77
140
78
141
#[ cfg( test) ]
79
142
mod test {
80
143
use super :: * ;
81
144
145
+ #[ test]
146
+ fn setup_parser_correct ( ) {
147
+ let test = r#"
148
+ [fcp_behaviors]
149
+
150
+ [fcp_behaviors."rust-lang/alpha"]
151
+ close = true
152
+ postpone = true
153
+
154
+ [fcp_behaviors."foobar/beta"]
155
+ close = false
156
+
157
+ [fcp_behaviors."bazquux/gamma"]
158
+ postpone = false
159
+
160
+ [fcp_behaviors."wibble/epsilon"]
161
+
162
+ [teams]
163
+
164
+ [teams.avengers]
165
+ name = "The Avengers"
166
+ ping = "marvel/avengers"
167
+ members = [
168
+ "hulk",
169
+ "thor",
170
+ "thevision",
171
+ "blackwidow",
172
+ "spiderman",
173
+ "captainamerica",
174
+ ]
175
+
176
+ [teams.justice-league]
177
+ name = "Justice League of America"
178
+ ping = "dc-comics/justice-league"
179
+ members = [
180
+ "superman",
181
+ "wonderwoman",
182
+ "aquaman",
183
+ "batman",
184
+ "theflash"
185
+ ]
186
+ "# ;
187
+ let cfg = read_rfcbot_cfg_from ( test) ;
188
+
189
+ // Labels are correct:
190
+ assert_eq ! ( cfg. team_labels( ) . map( |tl| tl. 0 . clone( ) ) . collect:: <Vec <_>>( ) ,
191
+ vec![ "avengers" , "justice-league" ] ) ;
192
+
193
+ // Teams are correct:
194
+ let map: BTreeMap < _ , _ > =
195
+ cfg. teams ( ) . map ( |( k, v) | ( k. 0 . clone ( ) , v. clone ( ) ) ) . collect ( ) ;
196
+
197
+ let avengers = map. get ( "avengers" ) . unwrap ( ) ;
198
+ assert_eq ! ( avengers. name, "The Avengers" ) ;
199
+ assert_eq ! ( avengers. ping, "marvel/avengers" ) ;
200
+ assert_eq ! ( avengers. member_logins( ) . collect:: <Vec <_>>( ) ,
201
+ vec![ "hulk" , "thor" , "thevision" , "blackwidow" ,
202
+ "spiderman" , "captainamerica" ] ) ;
203
+
204
+ let jsa = map. get ( "justice-league" ) . unwrap ( ) ;
205
+ assert_eq ! ( jsa. name, "Justice League of America" ) ;
206
+ assert_eq ! ( jsa. ping, "dc-comics/justice-league" ) ;
207
+ assert_eq ! ( jsa. member_logins( ) . collect:: <Vec <_>>( ) ,
208
+ vec![ "superman" , "wonderwoman" , "aquaman" , "batman" , "theflash" ] ) ;
209
+
210
+ // FFCP behavior correct:
211
+ assert ! ( cfg. ffcp_auto_close( "rust-lang/alpha" ) ) ;
212
+ assert ! ( cfg. ffcp_auto_postpone( "rust-lang/alpha" ) ) ;
213
+ assert ! ( !cfg. ffcp_auto_close( "foobar/beta" ) ) ;
214
+ assert ! ( !cfg. ffcp_auto_postpone( "foobar/beta" ) ) ;
215
+ assert ! ( !cfg. ffcp_auto_close( "bazquux/gamma" ) ) ;
216
+ assert ! ( !cfg. ffcp_auto_postpone( "bazquux/gamma" ) ) ;
217
+ assert ! ( !cfg. ffcp_auto_close( "wibble/epsilon" ) ) ;
218
+ assert ! ( !cfg. ffcp_auto_postpone( "wibble/epsilon" ) ) ;
219
+ assert ! ( !cfg. ffcp_auto_close( "random" ) ) ;
220
+ assert ! ( !cfg. ffcp_auto_postpone( "random" ) ) ;
221
+ }
222
+
223
+ #[ test]
224
+ fn cfg_file_wellformed ( ) {
225
+ // Just parse it and ensure that we get no panics for now!
226
+ // This is a crap test; but, better than nothing.
227
+ let _ = read_rfcbot_cfg ( ) ;
228
+ }
229
+
82
230
#[ test]
83
231
fn team_members_exist ( ) {
84
- for ( label, team ) in TEAMS . iter ( ) {
232
+ for ( label, _ ) in SETUP . teams . iter ( ) {
85
233
println ! ( "found team {:?}" , label) ;
86
234
}
87
235
}
0 commit comments