use std::fs;
use std::path::Path;
const COMMENT_CHARS: [&str; 2] = ["#", ";"];
pub struct Config {
pub data: Vec<[String; 2]>,
}
#[derive(Debug)]
pub enum ConfigError {
FileReadError,
NoFileDefined,
InvalidConfig,
ParseError,
NoItem,
}
fn remove_comments(str: String) -> String {
let mut s = str.as_str();
for i in COMMENT_CHARS.iter() {
s = s.split(i).next().unwrap();
}
s.to_string()
}
impl Config {
pub fn new() -> Self {
Config { data: Vec::new() }
}
pub fn file<T>(self, file: T) -> Result<Self, ConfigError>
where
T: AsRef<Path>,
{
let contents = match fs::read_to_string(file) {
Ok(contents) => contents,
Err(_) => return Err(ConfigError::FileReadError),
};
let mut data = self.data;
data.append(&mut Config::parse(contents)?);
Ok(Self { data })
}
pub fn text<T>(self, text: T) -> Result<Self, ConfigError>
where
T: std::fmt::Display,
{
let data = Config::parse(text.to_string())?;
Ok(Self { data })
}
pub fn get<T>(&self, key: &str) -> Result<T, ConfigError>
where
T: core::str::FromStr,
{
let key = key.to_string().to_lowercase();
for i in self.data.iter().rev() {
if i[0] != key {
continue;
}
match i[1].parse() {
Ok(i) => return Ok(i),
Err(_) => return Err(ConfigError::ParseError),
}
}
Err(ConfigError::NoItem)
}
pub fn get_str(&self, key: &str) -> Result<String, ConfigError> {
let key = key.to_string().to_lowercase();
for i in self.data.iter().rev() {
if i[0] != key {
continue;
}
return Ok(i[1].to_string());
}
Err(ConfigError::NoItem)
}
fn parse(input_data: String) -> Result<Vec<[String; 2]>, ConfigError> {
let mut done: Vec<[String; 2]> = Vec::new();
for line in input_data.lines() {
let mut line = line.trim().to_string();
match line.chars().next() {
Some(i) if COMMENT_CHARS.contains(&&i.to_string()[..]) => continue,
Some('[') => continue,
Some(_) => {}
None => continue,
}
line = remove_comments(line.to_string());
let parts: Vec<&str> = line.split('=').collect();
if parts.len() != 2 {
return Err(ConfigError::InvalidConfig);
}
let key = parts[0].replace(" ", "").to_lowercase();
let value = parts[1].trim().to_string();
done.push([key, value]);
}
Ok(done)
}
}
impl Default for Config {
fn default() -> Config {
Config::new()
}
}