3
3
use glob:: glob;
4
4
use rustc_serialize:: { Decodable , Decoder } ;
5
5
use std:: collections:: BTreeSet ;
6
+ use std:: fmt;
6
7
use std:: fs:: { self , File } ;
7
8
use std:: io;
8
9
use std:: io:: prelude:: * ;
9
10
use std:: path:: Path ;
10
11
use sysfs_gpio;
11
12
use toml;
12
13
14
+ const DEFAULT_SYMLINK_ROOT : & ' static str = "/var/run/gpio" ;
15
+
13
16
#[ derive( Debug , PartialEq , Clone ) ]
14
17
pub struct Direction ( pub sysfs_gpio:: Direction ) ;
15
18
@@ -28,9 +31,10 @@ pub struct PinConfig {
28
31
pub active_low : bool ,
29
32
}
30
33
31
- #[ derive( RustcDecodable , Clone , Debug ) ]
34
+ #[ derive( Clone , Debug , Default ) ]
32
35
pub struct GpioConfig {
33
- pub pins : Vec < PinConfig > ,
36
+ pins : Vec < PinConfig > ,
37
+ symlink_root : Option < String > ,
34
38
}
35
39
36
40
#[ derive( Debug ) ]
@@ -41,6 +45,22 @@ pub enum Error {
41
45
NoConfigFound ,
42
46
}
43
47
48
+ impl fmt:: Display for Error {
49
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
50
+ match * self {
51
+ Error :: IoError ( ref e) => e. fmt ( f) ,
52
+ Error :: ParserErrors ( ref errors) => {
53
+ for e in errors {
54
+ try!( e. fmt ( f) ) ;
55
+ }
56
+ Ok ( ( ) )
57
+ }
58
+ Error :: DecodingError ( ref e) => e. fmt ( f) ,
59
+ Error :: NoConfigFound => write ! ( f, "No Config Found" ) ,
60
+ }
61
+ }
62
+ }
63
+
44
64
impl From < io:: Error > for Error {
45
65
fn from ( e : io:: Error ) -> Self {
46
66
Error :: IoError ( e)
@@ -59,19 +79,37 @@ impl From<toml::DecodeError> for Error {
59
79
}
60
80
}
61
81
82
+ impl Decodable for GpioConfig {
83
+ fn decode < D : Decoder > ( d : & mut D ) -> Result < Self , D :: Error > {
84
+ // Get items under the [config] header if present
85
+ let symlink_root: Option < String > = d. read_struct_field ( "config" , 0 , |cfg| {
86
+ cfg. read_struct_field ( "symlink_root" ,
87
+ 0 ,
88
+ Decodable :: decode)
89
+ } )
90
+ . ok ( ) ;
91
+
92
+ Ok ( GpioConfig {
93
+ pins : try!( d. read_struct_field ( "pins" , 0 , Decodable :: decode) ) ,
94
+ symlink_root : symlink_root,
95
+ } )
96
+ }
97
+ }
98
+
62
99
impl Decodable for PinConfig {
63
100
fn decode < D : Decoder > ( d : & mut D ) -> Result < PinConfig , D :: Error > {
64
101
Ok ( PinConfig {
65
102
num : try!( d. read_struct_field ( "num" , 0 , Decodable :: decode) ) ,
66
103
direction : d. read_struct_field ( "direction" , 0 , |dir_d| {
67
- match & try!( dir_d. read_str ( ) ) [ ..] {
68
- "in" => Ok ( sysfs_gpio:: Direction :: In ) ,
69
- "out" => Ok ( sysfs_gpio:: Direction :: Out ) ,
70
- "high" => Ok ( sysfs_gpio:: Direction :: High ) ,
71
- "low" => Ok ( sysfs_gpio:: Direction :: Low ) ,
72
- _ => Err ( dir_d. error ( "Expected one of: {in, out, high, low}" ) ) ,
73
- }
74
- } ) . unwrap_or ( sysfs_gpio:: Direction :: In ) , // default: In
104
+ match & try!( dir_d. read_str ( ) ) [ ..] {
105
+ "in" => Ok ( sysfs_gpio:: Direction :: In ) ,
106
+ "out" => Ok ( sysfs_gpio:: Direction :: Out ) ,
107
+ "high" => Ok ( sysfs_gpio:: Direction :: High ) ,
108
+ "low" => Ok ( sysfs_gpio:: Direction :: Low ) ,
109
+ _ => Err ( dir_d. error ( "Expected one of: {in, out, high, low}" ) ) ,
110
+ }
111
+ } )
112
+ . unwrap_or ( sysfs_gpio:: Direction :: In ) , // default: In
75
113
names : d. read_struct_field ( "names" , 0 , Decodable :: decode) . unwrap_or ( BTreeSet :: new ( ) ) ,
76
114
export : d. read_struct_field ( "export" , 0 , Decodable :: decode) . unwrap_or ( true ) ,
77
115
active_low : d. read_struct_field ( "active_low" , 0 , Decodable :: decode) . unwrap_or ( false ) ,
@@ -80,7 +118,6 @@ impl Decodable for PinConfig {
80
118
}
81
119
82
120
impl GpioConfig {
83
-
84
121
/// Load a GPIO Config from the system
85
122
///
86
123
/// This function will load the GPIO configuration from standard system
@@ -145,6 +182,17 @@ impl GpioConfig {
145
182
GpioConfig :: from_str ( & contents[ ..] )
146
183
}
147
184
185
+ pub fn get_pins ( & self ) -> & [ PinConfig ] {
186
+ & self . pins [ ..]
187
+ }
188
+
189
+ pub fn get_symlink_root ( & self ) -> & str {
190
+ match self . symlink_root {
191
+ Some ( ref root) => & root,
192
+ None => DEFAULT_SYMLINK_ROOT ,
193
+ }
194
+ }
195
+
148
196
/// Merge other into self (takes ownership of other)
149
197
///
150
198
/// If in conflict, the other GPIO config takes priority.
@@ -158,14 +206,13 @@ impl GpioConfig {
158
206
pin. export = other_pin. export ;
159
207
pin. active_low = other_pin. active_low ;
160
208
true
161
- } ,
209
+ }
162
210
None => false ,
163
211
} ;
164
212
165
213
if !existing {
166
214
self . pins . push ( other_pin) ;
167
215
}
168
-
169
216
}
170
217
}
171
218
}
@@ -177,8 +224,7 @@ mod test {
177
224
use std:: collections:: BTreeSet ;
178
225
use sysfs_gpio:: Direction as D ;
179
226
180
- static BASIC_CFG : & ' static str = r#"
181
- # Basic Form
227
+ const BASIC_CFG : & ' static str = r#"
182
228
[[pins]]
183
229
num = 73
184
230
names = ["reset_button"]
@@ -192,19 +238,22 @@ names = ["status_led", "A27", "green_led"]
192
238
direction = "out"
193
239
"# ;
194
240
195
- static COMPACT_CFG : & ' static str = r#"
241
+ const COMPACT_CFG : & ' static str = r#"
196
242
pins = [
197
243
{ num = 73, names = ["reset_button"], direction = "in", active_low = true, export = true},
198
244
{ num = 37, names = ["status_led", "A27", "green_led"], direction = "out"},
199
245
]
246
+
247
+ [config]
248
+ symlink_root = "/tmp/gpio"
200
249
"# ;
201
250
202
- static MISSING_PINNUM_CFG : & ' static str = r#"
251
+ const MISSING_PINNUM_CFG : & ' static str = r#"
203
252
[[pins]]
204
253
export = true
205
254
"# ;
206
255
207
- static PARTIALLY_OVERLAPS_BASIC_CFG : & ' static str = r#"
256
+ const PARTIALLY_OVERLAPS_BASIC_CFG : & ' static str = r#"
208
257
# Add a new alias to pin 73
209
258
[[pins]]
210
259
num = 73
@@ -226,15 +275,16 @@ names = ["wildcard"]
226
275
fn test_parse_basic ( ) {
227
276
let config = GpioConfig :: from_str ( BASIC_CFG ) . unwrap ( ) ;
228
277
let status_led = config. pins . get ( 1 ) . unwrap ( ) ;
229
- let names = BTreeSet :: from_iter (
230
- vec ! ( String :: from( "status_led" ) ,
231
- String :: from( "A27" ) ,
232
- String :: from( "green_led" ) ) ) ;
278
+ let names = BTreeSet :: from_iter ( vec ! [ String :: from( "status_led" ) ,
279
+ String :: from( "A27" ) ,
280
+ String :: from( "green_led" ) ] ) ;
281
+
282
+ assert_eq ! ( config. get_symlink_root( ) , "/var/run/gpio" ) ;
233
283
234
284
let reset_button = config. pins . get ( 0 ) . unwrap ( ) ;
235
285
assert_eq ! ( reset_button. num, 73 ) ;
236
286
assert_eq ! ( reset_button. names,
237
- BTreeSet :: from_iter( vec!( String :: from( "reset_button" ) ) ) ) ;
287
+ BTreeSet :: from_iter( vec![ String :: from( "reset_button" ) ] ) ) ;
238
288
assert_eq ! ( reset_button. direction, D :: In ) ;
239
289
assert_eq ! ( reset_button. active_low, true ) ;
240
290
assert_eq ! ( reset_button. export, true ) ;
@@ -249,29 +299,29 @@ names = ["wildcard"]
249
299
fn test_parser_compact ( ) {
250
300
let config = GpioConfig :: from_str ( COMPACT_CFG ) . unwrap ( ) ;
251
301
let status_led = config. pins . get ( 1 ) . unwrap ( ) ;
252
- let names = BTreeSet :: from_iter (
253
- vec ! ( String :: from( "status_led" ) ,
254
- String :: from( "A27" ) ,
255
- String :: from( "green_led" ) ) ) ;
302
+ let names = BTreeSet :: from_iter ( vec ! [ String :: from( "status_led" ) ,
303
+ String :: from( "A27" ) ,
304
+ String :: from( "green_led" ) ] ) ;
256
305
assert_eq ! ( status_led. names, names) ;
257
306
assert_eq ! ( status_led. direction, D :: Out ) ;
258
307
assert_eq ! ( status_led. active_low, false ) ;
259
308
assert_eq ! ( status_led. export, true ) ;
309
+ assert_eq ! ( config. get_symlink_root( ) , "/tmp/gpio" )
260
310
}
261
311
262
312
#[ test]
263
313
fn test_parser_empty_toml ( ) {
264
314
let configstr = "" ;
265
315
match GpioConfig :: from_str ( configstr) {
266
- Ok ( pins) => { assert_eq ! ( pins. pins, vec!( ) ) } ,
316
+ Ok ( pins) => assert_eq ! ( pins. pins, vec![ ] ) ,
267
317
_ => panic ! ( "Expected a decoding error" ) ,
268
318
}
269
319
}
270
320
271
321
#[ test]
272
322
fn test_parser_missing_pinnum ( ) {
273
323
match GpioConfig :: from_str ( MISSING_PINNUM_CFG ) {
274
- Err ( Error :: DecodingError ( _) ) => { } ,
324
+ Err ( Error :: DecodingError ( _) ) => { }
275
325
_ => panic ! ( "Expected a decoding error" ) ,
276
326
}
277
327
}
@@ -281,7 +331,7 @@ names = ["wildcard"]
281
331
// basically, just garbage data
282
332
let configstr = r"[] -*-..asdf=-=-@#$%^&*()" ;
283
333
match GpioConfig :: from_str ( configstr) {
284
- Err ( Error :: ParserErrors ( _) ) => { } ,
334
+ Err ( Error :: ParserErrors ( _) ) => { }
285
335
_ => panic ! ( "Did not receive parse error when expected" ) ,
286
336
}
287
337
}
@@ -296,25 +346,25 @@ names = ["wildcard"]
296
346
297
347
let reset_button = config. pins . get ( 0 ) . unwrap ( ) ;
298
348
assert_eq ! ( reset_button. num, 73 ) ;
299
- assert_eq ! ( reset_button. names, BTreeSet :: from_iter (
300
- vec!( String :: from( "reset_button" ) ,
301
- String :: from( "new_name" ) ) ) ) ;
349
+ assert_eq ! ( reset_button. names,
350
+ BTreeSet :: from_iter ( vec![ String :: from( "reset_button" ) ,
351
+ String :: from( "new_name" ) ] ) ) ;
302
352
assert_eq ! ( reset_button. direction, D :: In ) ;
303
353
assert_eq ! ( reset_button. active_low, false ) ;
304
354
assert_eq ! ( reset_button. export, true ) ;
305
355
306
356
let status_led = config. pins . get ( 1 ) . unwrap ( ) ;
307
- let names = BTreeSet :: from_iter (
308
- vec ! ( String :: from( "status_led" ) ,
309
- String :: from( "A27" ) ,
310
- String :: from( "green_led" ) ) ) ;
357
+ let names = BTreeSet :: from_iter ( vec ! [ String :: from( "status_led" ) ,
358
+ String :: from( "A27" ) ,
359
+ String :: from( "green_led" ) ] ) ;
311
360
assert_eq ! ( status_led. names, names) ;
312
361
assert_eq ! ( status_led. direction, D :: In ) ;
313
362
assert_eq ! ( status_led. active_low, false ) ;
314
363
assert_eq ! ( status_led. export, true ) ;
315
364
316
365
let wildcard = config. pins . get ( 2 ) . unwrap ( ) ;
317
366
assert_eq ! ( wildcard. num, 88 ) ;
318
- assert_eq ! ( wildcard. names, BTreeSet :: from_iter( vec!( String :: from( "wildcard" ) ) ) ) ;
367
+ assert_eq ! ( wildcard. names,
368
+ BTreeSet :: from_iter( vec![ String :: from( "wildcard" ) ] ) ) ;
319
369
}
320
370
}
0 commit comments