#![deny(missing_docs)]
use header_parsing::parse_header;
use indexmap::IndexMap;
use thiserror::Error;
use std::{
fs::File,
io::{BufRead, BufReader},
path::Path,
};
#[derive(Debug, Error)]
pub enum Error {
#[error("Failed to open the configuration file")]
OpeningFile,
#[error("Subheader found without a corresponding header")]
SubheaderWithoutHeader,
#[error("Multiple values found for key: {0}")]
MultipleKeys(Box<str>),
#[error("Input issues")]
Input,
}
impl Error {
pub fn print_message(&self, path: &Path) {
use Error::*;
match self {
OpeningFile => eprintln!("Opening file {path:?} failed"),
SubheaderWithoutHeader => {
eprintln!("File {path:?} has a subheader without a header")
}
MultipleKeys(key) => eprintln!("Path {path:?} has a duplicate of key {key}"),
Input => eprintln!("Input error while reading {path:?}"),
}
}
}
pub fn parse_config(path: &Path) -> Result<IndexMap<Box<str>, Box<str>>, Error> {
let Ok(file) = File::open(path) else {
return Err(Error::OpeningFile);
};
let reader = BufReader::new(file);
let mut map = IndexMap::new();
let mut key_path = Vec::new();
for line in reader.lines() {
let Ok(line) = line else {
return Err(Error::Input);
};
if let Some(success) = parse_header(&mut key_path, &line) {
let Ok(changes) = success else {
return Err(Error::SubheaderWithoutHeader);
};
changes.apply();
continue;
}
let line = line.trim();
if line.is_empty() {
continue;
}
let (key, value) = line
.split_once(char::is_whitespace)
.map_or_else(|| (line, ""), |(key, value)| (key, value.trim_start()));
let key_path = key_path
.iter()
.rev()
.fold(String::new(), |result, new| format!("{new}:{result}"));
let full_key = format!("{key_path}{key}").into();
if map.contains_key(&full_key) {
return Err(Error::MultipleKeys(full_key));
}
map.insert(full_key, value.into());
}
Ok(map)
}