204 lines
6 KiB
Rust
204 lines
6 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use std::fmt::Display;
|
|
use std::fs::File;
|
|
use std::io::{BufRead, BufReader, Write};
|
|
use std::str::FromStr;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Clone)]
|
|
pub enum Value {
|
|
Value(String),
|
|
Array(Vec<String>),
|
|
}
|
|
|
|
struct ConfigSection {
|
|
header: String,
|
|
body: HashMap<String, Value>,
|
|
}
|
|
|
|
impl Value {
|
|
pub fn empty() -> Value {
|
|
Value::Value("".to_string())
|
|
}
|
|
|
|
pub fn value<T>(value: &T) -> Value
|
|
where
|
|
T: Display,
|
|
{
|
|
Value::Value(format!("{}", value))
|
|
}
|
|
|
|
pub fn array<T>(array: &Vec<T>) -> Value
|
|
where
|
|
T: Display,
|
|
{
|
|
Value::Array(array.iter().map(|v| format!("{}", v)).collect())
|
|
}
|
|
|
|
pub fn set_value<T>(&self, value: &mut T) -> Result<(), String>
|
|
where
|
|
T: FromStr,
|
|
{
|
|
match self {
|
|
Value::Value(value_string) => match value_string.parse::<T>() {
|
|
Ok(val) => *value = val,
|
|
Err(_) => return Err(format!("error parsing value {}", value_string)),
|
|
},
|
|
_ => return Err("key_value has wrong format".to_string()),
|
|
};
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn set_array<T>(&self, array: &mut Vec<T>) -> Result<(), String>
|
|
where
|
|
T: FromStr,
|
|
{
|
|
match self {
|
|
Value::Array(value_array) => {
|
|
for value_string in value_array {
|
|
match value_string.parse::<T>() {
|
|
Ok(val) => array.push(val),
|
|
Err(_) => return Err(format!("error parsing value {}", value_string)),
|
|
}
|
|
}
|
|
}
|
|
_ => return Err("key_value has wrong format".to_string()),
|
|
};
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub fn read_config(file_name: &str) -> Result<HashMap<String, HashMap<String, Value>>, String> {
|
|
let file = match File::open(file_name) {
|
|
Ok(file) => file,
|
|
Err(msg) => return Err(format!("error opening config file({}): {}", file_name, msg)),
|
|
};
|
|
|
|
let mut infos = HashMap::new();
|
|
let mut current_section: Option<ConfigSection> = None;
|
|
|
|
for line_res in BufReader::new(file).lines() {
|
|
if let Ok(line) = line_res {
|
|
let mut trimmed = line.trim().to_string();
|
|
|
|
if trimmed.starts_with("#") || trimmed.is_empty() {
|
|
continue;
|
|
} else if trimmed.starts_with("[") && trimmed.ends_with("]") {
|
|
trimmed.remove(0);
|
|
trimmed.pop();
|
|
|
|
if let Some(ref section) = current_section {
|
|
infos.insert(section.header.clone(), section.body.clone());
|
|
}
|
|
|
|
current_section = Some(ConfigSection {
|
|
header: trimmed,
|
|
body: HashMap::new(),
|
|
});
|
|
} else {
|
|
let mut split = trimmed.split("=");
|
|
|
|
let key = match split.nth(0) {
|
|
Some(key) => key.trim().to_string(),
|
|
None => {
|
|
println!("cannot get key from line: {}", trimmed);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
let value = match split.last() {
|
|
Some(value) => value.trim().to_string(),
|
|
None => {
|
|
println!("cannot get value from line: {}", trimmed);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
if value.starts_with("[") && value.ends_with("]") {
|
|
let mut trimmed_value = value;
|
|
trimmed_value.remove(0);
|
|
trimmed_value.pop();
|
|
|
|
let value_split = trimmed_value.split(";");
|
|
let value_array = value_split.map(|v| v.trim().to_string()).collect();
|
|
|
|
if let Some(ref mut section) = current_section {
|
|
section.body.insert(key, Value::Array(value_array));
|
|
}
|
|
} else {
|
|
if let Some(ref mut section) = current_section {
|
|
section.body.insert(key, Value::Value(value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// also push the last section
|
|
if let Some(section) = current_section {
|
|
infos.insert(section.header, section.body);
|
|
}
|
|
|
|
Ok(infos)
|
|
}
|
|
|
|
pub fn write_config(
|
|
file_name: &str,
|
|
sections: &Vec<(String, Vec<(String, Value)>)>,
|
|
) -> Result<(), String> {
|
|
let mut file = match File::create(file_name) {
|
|
Ok(file) => file,
|
|
Err(msg) => {
|
|
return Err(format!(
|
|
"error creating config file({}): {}",
|
|
file_name, msg
|
|
))
|
|
}
|
|
};
|
|
|
|
for (header, body) in sections {
|
|
let fmt_header = format!("[{}]\n", header);
|
|
|
|
if let Err(_) = file.write_all(fmt_header.as_bytes()) {
|
|
return Err(format!("failed writing section: {}", fmt_header));
|
|
}
|
|
|
|
for (key, value) in body {
|
|
let fmt_key_value = format!(
|
|
"{} = {}\n",
|
|
key,
|
|
match value {
|
|
Value::Value(val) => val.clone(),
|
|
Value::Array(array) => {
|
|
let mut array_value = "[".to_string();
|
|
|
|
for (i, val) in array.iter().enumerate() {
|
|
// if element is not the last one
|
|
if i != array.len() - 1 {
|
|
array_value = format!("{}{}, ", array_value, val);
|
|
} else {
|
|
array_value = format!("{}{}", array_value, val);
|
|
}
|
|
}
|
|
|
|
format!("{}]", array_value)
|
|
}
|
|
}
|
|
);
|
|
|
|
if let Err(_) = file.write_all(fmt_key_value.as_bytes()) {
|
|
return Err(format!("failed writing key value: {}", fmt_key_value));
|
|
}
|
|
}
|
|
|
|
if let Err(_) = file.write_all("\n".as_bytes()) {
|
|
return Err("failed writing new line".to_string());
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|