use alloc::{format, string::String};
use ini::Ini;
use crate::fs::*;
pub mod ini;
pub trait IniDeserialize: Sized {
fn from_ini_section(ini: &Ini, base: &str) -> Self;
fn from_ini(ini: &Ini) -> Self {
Self::from_ini_section(ini, "")
}
}
pub trait IniSerialize {
fn to_ini_section(&self, ini: &mut Ini, base: &str);
fn to_ini(&self) -> Ini {
let mut ini = Ini::default();
self.to_ini_section(&mut ini, "");
ini
}
}
pub fn load<T: IniDeserialize + IniSerialize + Default>(filename: &str) -> Result<T, String> {
let _mount = savedata::ScopedMount::new();
let path = format!("{}:/{}\0", savedata::mount_point(), filename);
if file_exists(&path) {
let ini = ini::Reader::read(&path)?;
Ok(T::from_ini(&ini))
} else {
let config = T::default();
ini::Writer::write(&path, &config.to_ini())?;
Ok(config)
}
}
#[doc(hidden)]
pub mod __private {
use core::{marker::PhantomData, str::FromStr};
use alloc::{
format,
string::{String, ToString},
};
use super::{IniDeserialize, IniSerialize, ini::Ini};
pub fn join(base: &str, segment: &str) -> String {
if base.is_empty() {
String::from(segment)
} else if segment.is_empty() {
String::from(base)
} else {
format!("{base}.{segment}")
}
}
pub struct Probe<T>(pub PhantomData<T>);
pub trait IniNestedField {
type Out;
fn __ini_read(&self, ini: &Ini, section: &str, key: &str) -> Self::Out;
}
impl<T: IniDeserialize> IniNestedField for Probe<T> {
type Out = T;
fn __ini_read(&self, ini: &Ini, section: &str, key: &str) -> T {
T::from_ini_section(ini, &join(section, key))
}
}
pub trait IniScalarField {
type Out;
fn __ini_read(&self, ini: &Ini, section: &str, key: &str) -> Self::Out;
}
impl<T: FromStr + Default> IniScalarField for &Probe<T> {
type Out = T;
fn __ini_read(&self, ini: &Ini, section: &str, key: &str) -> T {
ini.get_as::<T>(section, key, T::default())
}
}
pub trait IniNestedWrite {
type In;
fn __ini_write(&self, value: &Self::In, ini: &mut Ini, section: &str, key: &str);
}
impl<T: IniSerialize> IniNestedWrite for Probe<T> {
type In = T;
fn __ini_write(&self, value: &T, ini: &mut Ini, section: &str, key: &str) {
value.to_ini_section(ini, &join(section, key));
}
}
pub trait IniScalarWrite {
type In;
fn __ini_write(&self, value: &Self::In, ini: &mut Ini, section: &str, key: &str);
}
impl<T: ToString> IniScalarWrite for &Probe<T> {
type In = T;
fn __ini_write(&self, value: &T, ini: &mut Ini, section: &str, key: &str) {
ini.set(section, key, value.to_string());
}
}
}