use std::fs::File;
use std::io;
use std::io::{BufRead, BufReader};
#[inline]
pub fn get_ver() -> String {
String::from(env!("CARGO_PKG_VERSION"))
}
#[derive(Debug, PartialEq)]
pub struct OptionProperties {
pub option: String,
pub value: Value,
}
#[derive(Debug, PartialEq)]
pub struct Value {
pub primary: String,
pub attributes: Vec<String>,
}
impl OptionProperties {
fn new(option: String, primary: String, attributes: Vec<String>) -> Self {
Self {
option,
value: Value {
primary,
attributes,
},
}
}
}
#[inline]
pub fn parse_file(filename: &str, attr_delimit_char: char) -> io::Result<Vec<OptionProperties>> {
let file = File::open(filename)?;
let reader = BufReader::new(file);
let mut vec: Vec<OptionProperties> = Vec::new();
for (line_num, line) in reader.lines().enumerate() {
let (option, primary_value, attr_vec) =
parse_line(&(line?), attr_delimit_char, line_num + 1);
if !option.is_empty() {
let opt_props = OptionProperties::new(option, primary_value, attr_vec);
vec.push(opt_props);
}
}
Ok(vec)
}
fn parse_line(l: &str, attr_delimit_char: char, ln: usize) -> (String, String, Vec<String>) {
let line = l.trim();
if line.is_empty() || line.as_bytes()[0] == b'#' {
return ("".to_string(), "".to_string(), vec![]);
}
let mut i = line.find('=');
let (mut option, value) = match i.is_some() {
true => (
line[..i.unwrap()].trim().to_string(),
line[i.unwrap() + 1..].trim().to_string(),
),
false => (line.to_string(), String::new()),
};
let o = &option;
for c in o.chars() {
if c.is_whitespace() {
option = format!("{}_on_Line{}", "InvalidOption".to_string(), ln);
return (option, "".to_string(), vec![]);
}
}
i = value.find(attr_delimit_char);
let primary_value;
let mut tmp_attr_vec: Vec<&str> = Vec::new();
let attributes;
match i.is_some() {
true => {
primary_value = value[..i.unwrap()].trim().to_string();
attributes = value[i.unwrap() + 1..].to_string();
tmp_attr_vec = attributes.split(attr_delimit_char).collect();
}
false => primary_value = value,
}
let mut attr_vec: Vec<String> = Vec::new();
for a in &tmp_attr_vec {
attr_vec.push(a.trim().to_string());
}
(option, primary_value, attr_vec)
}
#[test]
fn test_parse_file() {
let line1 = OptionProperties::new(
"option".to_string(),
"Blue".to_string(),
vec!["light".to_string(), "shiny".to_string()],
);
let line2 = OptionProperties::new("max_users".to_string(), "30".to_string(), vec![]);
let line3 = OptionProperties::new("DelayOff".to_string().to_string(), "".to_string(), vec![]);
let invalid_option = OptionProperties::new(
"InvalidOption_on_Line8".to_string().to_string(),
"".to_string(),
vec![],
);
assert_eq!(
parse_file("./config_test.conf", ',').unwrap(),
vec![line1, line2, line3, invalid_option]
);
}
#[test]
fn test_parse_line() {
assert_eq!(
parse_line("Option = /home/foo", ',', 0),
("Option".to_string(), "/home/foo".to_string(), vec![])
);
assert_eq!(
parse_line("Option=/home/foo , another , test,1,2,3", ',', 0),
(
"Option".to_string(),
"/home/foo".to_string(),
vec![
"another".to_string(),
"test".to_string(),
"1".to_string(),
"2".to_string(),
"3".to_string()
]
)
);
assert_eq!(
parse_line("#Option = /home/foo", ',', 0),
("".to_string(), "".to_string(), vec![])
);
assert_eq!(
parse_line("Option = /home/foo, removable, test", ',', 0),
(
"Option".to_string(),
"/home/foo".to_string(),
vec!["removable".to_string(), "test".to_string()]
)
);
assert_eq!(
parse_line(" ", ',', 0),
("".to_string(), "".to_string(), vec![])
);
assert_eq!(
parse_line("Option /home/foo", ',', 28),
(
"InvalidOption_on_Line28".to_string(),
"".to_string(),
vec![]
)
);
assert_eq!(
parse_line("Option /home/foo = value", ',', 9),
("InvalidOption_on_Line9".to_string(), "".to_string(), vec![])
);
}