Implement config file reader
This commit is contained in:
parent
264acc6645
commit
7d02ff7f0a
5 changed files with 234 additions and 1 deletions
|
|
@ -8,3 +8,7 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
|
toml = "0.5"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
function_name = "0.2.0"
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,7 @@
|
||||||
# Start test if one of the files was modified
|
# Start test if one of the files was modified
|
||||||
cargo install cargo-watch
|
cargo install cargo-watch
|
||||||
|
|
||||||
RUST_BACKTRACE=full cargo watch -x "test -- --nocapture"
|
# Run the tests on change
|
||||||
|
# Set RUST_BACKTRACE to 1 to see a short backtrace in case of an error
|
||||||
|
# Set RUST_BACKTRACE to "full" to see a full backtrace in case of an error
|
||||||
|
RUST_BACKTRACE=0 cargo watch -x "test -- --nocapture"
|
||||||
|
|
|
||||||
52
src/cfg_reader.rs
Normal file
52
src/cfg_reader.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::{error::Error, io::Read};
|
||||||
|
|
||||||
|
extern crate toml;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
channels: Vec<S0Channel>,
|
||||||
|
// debounce_millis : Option<u8>,
|
||||||
|
// invert : Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct S0Channel {
|
||||||
|
id: u8,
|
||||||
|
gpio: u8,
|
||||||
|
// debounce_millis: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// use std::any::type_name;
|
||||||
|
// fn type_of<T>(_: T) -> &'static str {
|
||||||
|
// type_name::<T>()
|
||||||
|
// }
|
||||||
|
|
||||||
|
const MAX_CONFIG_FILE_SIZE: u64 = 1_000_000u64;
|
||||||
|
|
||||||
|
pub fn parse_file(config_file_name: &str) -> Result<Config, Box<dyn Error>> {
|
||||||
|
let mut cfg_file = File::open(String::from(config_file_name))?;
|
||||||
|
let cfg_file_meta = &cfg_file.metadata()?;
|
||||||
|
let cfg_file_len = &cfg_file_meta.len();
|
||||||
|
if cfg_file_len > &MAX_CONFIG_FILE_SIZE {
|
||||||
|
return Err(From::from(format!(
|
||||||
|
"Size of config file is {} exceeds the limit of {}",
|
||||||
|
&cfg_file_len, MAX_CONFIG_FILE_SIZE
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let mut cfg_file_content = String::new();
|
||||||
|
cfg_file.read_to_string(&mut cfg_file_content).unwrap();
|
||||||
|
parse_string(&cfg_file_content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_string(cfg_str: &String) -> Result<Config, Box<dyn Error>> {
|
||||||
|
match toml::from_str(cfg_str) {
|
||||||
|
Ok(cfg) => Ok(cfg),
|
||||||
|
Err(err) => Err(From::from(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "./cfg_reader_test.rs"]
|
||||||
|
mod cfg_reader_test;
|
||||||
172
src/cfg_reader_test.rs
Normal file
172
src/cfg_reader_test.rs
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
use super::*;
|
||||||
|
use function_name::named;
|
||||||
|
use std::{fs, io::Write};
|
||||||
|
|
||||||
|
fn create_cfg_file(fn_name: &str, content: &str) -> String {
|
||||||
|
let cfg_file_name = format!("/tmp/{}.cfg", fn_name);
|
||||||
|
let mut cfg_file = File::create(&cfg_file_name).unwrap();
|
||||||
|
cfg_file.write(content.as_bytes()).unwrap();
|
||||||
|
cfg_file_name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_cfg_file(cfg_file_name: &String) {
|
||||||
|
fs::remove_file(cfg_file_name).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_file_fails_if_file_does_not_exist() {
|
||||||
|
assert!(parse_file("/this/path/does/not/exist").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[named]
|
||||||
|
fn parse_file_fails_if_file_is_greater_than_1mb() {
|
||||||
|
let cfg_file_name = create_cfg_file(
|
||||||
|
function_name!(),
|
||||||
|
"a".repeat(MAX_CONFIG_FILE_SIZE as usize + 1).as_str(),
|
||||||
|
);
|
||||||
|
assert!(parse_file(&cfg_file_name).is_err());
|
||||||
|
remove_cfg_file(&cfg_file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[named]
|
||||||
|
fn parse_file_succeeds() {
|
||||||
|
let cfg_file_name = create_cfg_file(
|
||||||
|
function_name!(),
|
||||||
|
r#"
|
||||||
|
[[channels]]
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_file(&cfg_file_name).is_ok());
|
||||||
|
remove_cfg_file(&cfg_file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_fails_on_empty_string() {
|
||||||
|
let cfg_str = String::from(r#""#);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_fails_without_section_channels() {
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_fails_on_unknown_section() {
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
[[unknown]]
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_fails_on_section_format_error() {
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
[[channels]
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
[channels]
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
[[channels]]
|
||||||
|
id =
|
||||||
|
gpio = 4
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
[[channels]]
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
2
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_fails_on_missing_field_id() {
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
[[channels]]
|
||||||
|
gpio = 4
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_fails_on_missing_field_gpio() {
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
[[channels]]
|
||||||
|
id = 1
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_ignores_unknown_attributes() {
|
||||||
|
let cfg_str = String::from(
|
||||||
|
r#"
|
||||||
|
[[channels]]
|
||||||
|
unknown = 1
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert!(parse_string(&cfg_str).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_string_succeeds() {
|
||||||
|
let cfg = String::from(
|
||||||
|
r#"
|
||||||
|
[[channels]]
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
|
||||||
|
[[channels]]
|
||||||
|
id = 2
|
||||||
|
gpio = 17
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
let config = parse_string(&cfg);
|
||||||
|
assert!(&config.is_ok());
|
||||||
|
let config = config.unwrap();
|
||||||
|
assert_eq!(config.channels.len(), 2);
|
||||||
|
assert_eq!(config.channels[0].id, 1);
|
||||||
|
assert_eq!(config.channels[0].gpio, 4);
|
||||||
|
assert_eq!(config.channels[1].id, 2);
|
||||||
|
assert_eq!(config.channels[1].gpio, 17);
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
|
|
||||||
|
mod cfg_reader;
|
||||||
|
|
||||||
const APP_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
const APP_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
const DEFAULT_CONFIG_FILE_NAME: &str = "/etc/s0_logger.cfg";
|
const DEFAULT_CONFIG_FILE_NAME: &str = "/etc/s0_logger.cfg";
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue