// chain iterators together and collect the results let values: Vec<(String, String)> = buffer .split_terminator("\n") // split .map(|line| line.trim()) // remove whitespace .filter(|line| { // filter invalid lines let pos = line.find("=") .unwrap_or(0); pos > 0 && pos < line.len() - 1 }) .map(|line| { // create a tuple from a line let parts = line.split("=") .collect::<Vec<&str>>(); (parts[0].to_string(), parts[1].to_string()) }) .collect(); // transform it into a vector Ok(Config::new(values)) } }
impl ValueGetter for Config { fn get(&self, s: &str) -> Option<String> { self.values.iter() .find_map(|tuple| if &tuple.0 == s { Some(tuple.1.clone()) } else { None }) } }
Next, we need some tests to show it in action. To cover some basics, let's add best-case unit tests:
#[cfg(test)] mod tests { use super::*; use std::io::Cursor;
Lastly, we run cargo test and see that everything works out:
$ cargo test Compiling traits v0.1.0 (Rust-Cookbook/Chapter01/traits) Finished dev [unoptimized + debuginfo] target(s) in 0.92s Running target/debug/deps/traits-e1d367b025654a89
running 3 tests test tests::config_get_value ... ok test tests::keyvalueconfigservice_write_config ... ok test tests::keyvalueconfigservice_read_config ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Doc-tests traits
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Now, let's go behind the scenes to understand the code better.