use std::{fs, ops::Not, path::Path, str::FromStr};
use crate::{generate_builder_method, Entry, LibSDBootConfError};
#[derive(Default, Debug, PartialEq)]
pub struct Config {
pub default: Option<String>,
pub timeout: Option<u32>,
}
impl FromStr for Config {
type Err = LibSDBootConfError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut config = Self::default();
let lines = s.lines();
for line in lines {
if line.starts_with('#') || line.is_empty() {
continue;
}
let mut parts = line.splitn(2, ' ');
let key = parts.next().ok_or(LibSDBootConfError::ConfigParseError)?;
let value = parts.next().ok_or(LibSDBootConfError::ConfigParseError)?;
match key {
"default" => config.default = Some(value.to_string()),
"timeout" => config.timeout = Some(value.parse().unwrap_or_default()),
_ => continue,
}
}
Ok(config)
}
}
impl ToString for Config {
fn to_string(&self) -> String {
let mut buffer = String::new();
if let Some(default) = &self.default {
buffer.push_str(&format!("default {}\n", default));
}
if let Some(timeout) = &self.timeout {
buffer.push_str(&format!("timeout {}\n", timeout));
}
buffer
}
}
impl Config {
pub fn new<S, U>(default: Option<S>, timeout: Option<U>) -> Config
where
S: Into<String>,
U: Into<u32>,
{
Config {
default: default.map(|s| s.into()),
timeout: timeout.map(|u| u.into()),
}
}
pub fn load<P: AsRef<Path>>(path: P) -> Result<Config, LibSDBootConfError> {
Config::from_str(&fs::read_to_string(path.as_ref())?)
}
pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<(), LibSDBootConfError> {
fs::write(path.as_ref(), self.to_string())?;
Ok(())
}
pub fn set_default(&mut self, default: &Entry) {
self.default = Some(
default.id.clone()
+ default
.id
.ends_with(".conf")
.not()
.then_some(".conf")
.unwrap_or_default(),
);
}
pub fn default_entry<P: AsRef<Path>>(
&self,
directory: P,
) -> Result<Option<Entry>, LibSDBootConfError> {
self.default
.as_ref()
.map(|default| Entry::load(directory.as_ref().join(default)))
.transpose()
}
}
#[derive(Default, Debug)]
pub struct ConfigBuilder {
inner: Config,
}
impl ConfigBuilder {
pub fn new() -> Self {
Self {
inner: Config::default(),
}
}
generate_builder_method!(
option INNER(inner) default(S: String)
);
generate_builder_method!(
option INNER(inner) timeout(U: u32)
);
pub fn default_entry(mut self, entry: &Entry) -> Self {
self.inner.set_default(entry);
self
}
pub fn build(self) -> Config {
self.inner
}
}