2
2
3
3
use glob:: glob;
4
4
use rustc_serialize:: { Decodable , Decoder } ;
5
- use std:: collections:: BTreeSet ;
5
+ use std:: collections:: { HashMap , BTreeSet } ;
6
6
use std:: fmt;
7
7
use std:: fs:: { self , File } ;
8
8
use std:: io;
@@ -43,6 +43,7 @@ pub enum Error {
43
43
ParserErrors ( Vec < toml:: ParserError > ) ,
44
44
DecodingError ( toml:: DecodeError ) ,
45
45
NoConfigFound ,
46
+ DuplicateNames ( String ) ,
46
47
}
47
48
48
49
impl fmt:: Display for Error {
@@ -57,6 +58,7 @@ impl fmt::Display for Error {
57
58
}
58
59
Error :: DecodingError ( ref e) => e. fmt ( f) ,
59
60
Error :: NoConfigFound => write ! ( f, "No Config Found" ) ,
61
+ Error :: DuplicateNames ( ref e) => e. fmt ( f) ,
60
62
}
61
63
}
62
64
}
@@ -118,6 +120,28 @@ impl Decodable for PinConfig {
118
120
}
119
121
120
122
impl GpioConfig {
123
+ /// Validate invariants on the config that cannot easily be done earlier
124
+ ///
125
+ /// Currently, this just checks that there are no duplicated names between
126
+ /// different pins in the config
127
+ fn validate ( & self ) -> Result < ( ) , Error > {
128
+ let mut all_names: HashMap < & str , & PinConfig > = HashMap :: new ( ) ;
129
+ for pin in & self . pins {
130
+ for name in & pin. names {
131
+ if let Some ( other_pin) = all_names. get ( & name[ ..] ) {
132
+ return Err ( Error :: DuplicateNames ( format ! ( "Pins {} and {} share duplicate \
133
+ name '{}'",
134
+ pin. num,
135
+ other_pin. num,
136
+ name) ) ) ;
137
+ }
138
+ all_names. insert ( & name[ ..] , pin) ;
139
+ }
140
+ }
141
+
142
+ Ok ( ( ) )
143
+ }
144
+
121
145
/// Load a GPIO Config from the system
122
146
///
123
147
/// This function will load the GPIO configuration from standard system
@@ -158,7 +182,7 @@ impl GpioConfig {
158
182
} else {
159
183
let mut cfg = config_instances. remove ( 0 ) ;
160
184
for higher_priority_cfg in config_instances {
161
- cfg. update ( higher_priority_cfg) ;
185
+ try! ( cfg. update ( higher_priority_cfg) ) ;
162
186
}
163
187
Ok ( cfg)
164
188
}
@@ -168,8 +192,11 @@ impl GpioConfig {
168
192
pub fn from_str ( config : & str ) -> Result < GpioConfig , Error > {
169
193
let mut parser = toml:: Parser :: new ( config) ;
170
194
let root = try!( parser. parse ( ) . ok_or ( parser. errors ) ) ;
171
- match Decodable :: decode ( & mut toml:: Decoder :: new ( toml:: Value :: Table ( root) ) ) {
172
- Ok ( cfg) => Ok ( cfg) ,
195
+ match GpioConfig :: decode ( & mut toml:: Decoder :: new ( toml:: Value :: Table ( root) ) ) {
196
+ Ok ( cfg) => {
197
+ try!( cfg. validate ( ) . or_else ( |e| Err ( Error :: from ( e) ) ) ) ;
198
+ Ok ( cfg)
199
+ } ,
173
200
Err ( e) => Err ( Error :: from ( e) ) ,
174
201
}
175
202
}
@@ -179,7 +206,9 @@ impl GpioConfig {
179
206
let mut contents = String :: new ( ) ;
180
207
let mut f = try!( File :: open ( path) ) ;
181
208
try!( f. read_to_string ( & mut contents) ) ;
182
- GpioConfig :: from_str ( & contents[ ..] )
209
+ let config = try!( GpioConfig :: from_str ( & contents[ ..] ) ) ;
210
+ try!( config. validate ( ) ) ;
211
+ Ok ( config)
183
212
}
184
213
185
214
/// Get the pin with the provided name if present in this configuration
@@ -203,7 +232,7 @@ impl GpioConfig {
203
232
/// Merge other into self (takes ownership of other)
204
233
///
205
234
/// If in conflict, the other GPIO config takes priority.
206
- pub fn update ( & mut self , other : GpioConfig ) {
235
+ pub fn update ( & mut self , other : GpioConfig ) -> Result < ( ) , Error > {
207
236
if let Some ( symlink_root) = other. symlink_root {
208
237
self . symlink_root = Some ( symlink_root) ;
209
238
}
@@ -225,6 +254,9 @@ impl GpioConfig {
225
254
self . pins . push ( other_pin) ;
226
255
}
227
256
}
257
+
258
+ // validate the resulting structure
259
+ self . validate ( )
228
260
}
229
261
}
230
262
@@ -262,6 +294,16 @@ symlink_root = "/tmp/gpio"
262
294
const MISSING_PINNUM_CFG : & ' static str = r#"
263
295
[[pins]]
264
296
export = true
297
+ "# ;
298
+
299
+ const DUPLICATED_NAMES_CFG : & ' static str = r#"
300
+ [[pins]]
301
+ num = 25
302
+ names = ["foo", "bar"]
303
+
304
+ [[pins]]
305
+ num = 26
306
+ names = ["baz", "foo"] # foo is repeated!
265
307
"# ;
266
308
267
309
const PARTIALLY_OVERLAPS_BASIC_CFG : & ' static str = r#"
@@ -363,13 +405,21 @@ names = ["wildcard"]
363
405
}
364
406
}
365
407
408
+ #[ test]
409
+ fn test_error_on_duplicated_names ( ) {
410
+ match GpioConfig :: from_str ( DUPLICATED_NAMES_CFG ) {
411
+ Err ( Error :: DuplicateNames ( _) ) => ( ) ,
412
+ r => panic ! ( "Expected DuplicateNames Error, got {:?}" , r) ,
413
+ }
414
+ }
415
+
366
416
#[ test]
367
417
fn test_merge_configs ( ) {
368
418
let mut config = GpioConfig :: from_str ( BASIC_CFG ) . unwrap ( ) ;
369
419
let cfg2 = GpioConfig :: from_str ( PARTIALLY_OVERLAPS_BASIC_CFG ) . unwrap ( ) ;
370
420
371
421
// perform the merge
372
- config. update ( cfg2) ;
422
+ config. update ( cfg2) . unwrap ( ) ;
373
423
374
424
assert_eq ! ( config. get_symlink_root( ) , "/foo/bar/baz" ) ;
375
425
0 commit comments