Skip to content

Commit b340e95

Browse files
committed
config: add proper support for merging configs
Later config fragments are given higher priority and will override or merge lower-priority elements as appropriate. Signed-off-by: Paul Osborne <[email protected]>
1 parent 5b7136b commit b340e95

File tree

2 files changed

+101
-27
lines changed

2 files changed

+101
-27
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/config.rs

Lines changed: 99 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use glob::glob;
44
use rustc_serialize::{Decodable, Decoder};
5-
use std::collections::{BTreeSet};
5+
use std::collections::{BTreeSet, HashMap};
66
use std::fs::{self, File};
77
use std::io;
88
use std::io::prelude::*;
@@ -175,13 +175,28 @@ impl GpioConfig {
175175
GpioConfig::from_str(&contents[..])
176176
}
177177

178-
/// Merge this config with other yielding a new, merged version
178+
/// Merge other into self (takes ownership of other)
179179
///
180180
/// If in conflict, the other GPIO config takes priority.
181181
pub fn update(&mut self, other: GpioConfig) {
182-
// TODO: This needs to actually resolve conflicts rather than
183-
// blindly writing over as it does now.
184-
self.pins.extend(other.pins)
182+
for other_pin in other.pins {
183+
// determine the case we are dealing with
184+
let existing = match self.pins.iter_mut().find(|p| p.num == other_pin.num) {
185+
Some(pin) => {
186+
pin.names.extend(other_pin.names.clone());
187+
pin.direction = other_pin.direction.clone(); // TODO impl copy
188+
pin.export = other_pin.export;
189+
pin.active_low = other_pin.active_low;
190+
true
191+
},
192+
None => false,
193+
};
194+
195+
if !existing {
196+
self.pins.push(other_pin);
197+
}
198+
199+
}
185200
}
186201
}
187202

@@ -192,12 +207,9 @@ mod test {
192207
use std::collections::BTreeSet;
193208
use sysfs_gpio::Direction as D;
194209

195-
#[test]
196-
fn test_parse_basic() {
197-
let configstr = r#"
210+
static BASIC_CFG: &'static str = r#"
198211
# Basic Form
199212
[[pins]]
200-
201213
num = 73
202214
names = ["reset_button"]
203215
direction = "in" # default: in
@@ -209,12 +221,54 @@ num = 37
209221
names = ["status_led", "A27", "green_led"]
210222
direction = "out"
211223
"#;
212-
let config = GpioConfig::from_str(configstr).unwrap();
224+
225+
static COMPACT_CFG: &'static str = r#"
226+
pins = [
227+
{ num = 73, names = ["reset_button"], direction = "in", active_low = true, export = true},
228+
{ num = 37, names = ["status_led", "A27", "green_led"], direction = "out"},
229+
]
230+
"#;
231+
232+
static MISSING_PINNUM_CFG: &'static str = r#"
233+
[[pins]]
234+
export = true
235+
"#;
236+
237+
static PARTIALLY_OVERLAPS_BASIC_CFG: &'static str = r#"
238+
# Add a new alias to pin 73
239+
[[pins]]
240+
num = 73
241+
names = ["new_name"]
242+
243+
244+
# Change pin 37 to be an input (not output)
245+
[[pins]]
246+
num = 37
247+
direction = "in"
248+
249+
# New pin 88
250+
[[pins]]
251+
num = 88
252+
names = ["wildcard"]
253+
"#;
254+
255+
#[test]
256+
fn test_parse_basic() {
257+
let config = GpioConfig::from_str(BASIC_CFG).unwrap();
213258
let status_led = config.pins.get(1).unwrap();
214259
let names = BTreeSet::from_iter(
215260
vec!(String::from("status_led"),
216261
String::from("A27"),
217262
String::from("green_led")));
263+
264+
let reset_button = config.pins.get(0).unwrap();
265+
assert_eq!(reset_button.num, 73);
266+
assert_eq!(reset_button.names,
267+
BTreeSet::from_iter(vec!(String::from("reset_button"))));
268+
assert_eq!(reset_button.direction, D::In);
269+
assert_eq!(reset_button.active_low, true);
270+
assert_eq!(reset_button.export, true);
271+
218272
assert_eq!(status_led.names, names);
219273
assert_eq!(status_led.direction, D::Out);
220274
assert_eq!(status_led.active_low, false);
@@ -223,13 +277,7 @@ direction = "out"
223277

224278
#[test]
225279
fn test_parser_compact() {
226-
let configstr = r#"
227-
pins = [
228-
{ num = 73, names = ["reset_button"], direction = "in", active_low = true, export = true},
229-
{ num = 37, names = ["status_led", "A27", "green_led"], direction = "out"},
230-
]
231-
"#;
232-
let config = GpioConfig::from_str(configstr).unwrap();
280+
let config = GpioConfig::from_str(COMPACT_CFG).unwrap();
233281
let status_led = config.pins.get(1).unwrap();
234282
let names = BTreeSet::from_iter(
235283
vec!(String::from("status_led"),
@@ -252,11 +300,7 @@ pins = [
252300

253301
#[test]
254302
fn test_parser_missing_pinnum() {
255-
let configstr = r#"
256-
[[pins]]
257-
export = true
258-
"#;
259-
match GpioConfig::from_str(configstr) {
303+
match GpioConfig::from_str(MISSING_PINNUM_CFG) {
260304
Err(Error::DecodingError(_)) => {},
261305
_ => panic!("Expected a decoding error"),
262306
}
@@ -265,12 +309,42 @@ export = true
265309
#[test]
266310
fn test_parse_error_bad_toml() {
267311
// basically, just garbage data
268-
let configstr = r#"
269-
[] -*-..asdf=-=-@#$%^&*()
270-
"#;
312+
let configstr = r"[] -*-..asdf=-=-@#$%^&*()";
271313
match GpioConfig::from_str(configstr) {
272314
Err(Error::ParserErrors(e)) => {},
273315
_ => panic!("Did not receive parse error when expected"),
274316
}
275317
}
318+
319+
#[test]
320+
fn test_merge_configs() {
321+
let mut config = GpioConfig::from_str(BASIC_CFG).unwrap();
322+
let cfg2 = GpioConfig::from_str(PARTIALLY_OVERLAPS_BASIC_CFG).unwrap();
323+
324+
// perform the merge
325+
config.update(cfg2);
326+
327+
let reset_button = config.pins.get(0).unwrap();
328+
assert_eq!(reset_button.num, 73);
329+
assert_eq!(reset_button.names, BTreeSet::from_iter(
330+
vec!(String::from("reset_button"),
331+
String::from("new_name"))));
332+
assert_eq!(reset_button.direction, D::In);
333+
assert_eq!(reset_button.active_low, false);
334+
assert_eq!(reset_button.export, true);
335+
336+
let status_led = config.pins.get(1).unwrap();
337+
let names = BTreeSet::from_iter(
338+
vec!(String::from("status_led"),
339+
String::from("A27"),
340+
String::from("green_led")));
341+
assert_eq!(status_led.names, names);
342+
assert_eq!(status_led.direction, D::In);
343+
assert_eq!(status_led.active_low, false);
344+
assert_eq!(status_led.export, true);
345+
346+
let wildcard = config.pins.get(2).unwrap();
347+
assert_eq!(wildcard.num, 88);
348+
assert_eq!(wildcard.names, BTreeSet::from_iter(vec!(String::from("wildcard"))));
349+
}
276350
}

0 commit comments

Comments
 (0)