use std::collections::HashMap;
use std::io::{self, BufRead, Write};
use std::path::{Path, PathBuf};
pub struct Config {
path: PathBuf,
data: HashMap<String, String>,
}
impl Config {
pub fn load(game_dir: &str, mod_id: &str) -> Self {
let safe = mod_id.replace([':', '/'], "_");
let dir = Path::new(game_dir).join("yog-config");
let path = dir.join(format!("{safe}.cfg"));
let data = Self::parse(&path).unwrap_or_default();
Self { path, data }
}
fn parse(path: &Path) -> io::Result<HashMap<String, String>> {
let file = std::fs::File::open(path)?;
let mut map = HashMap::new();
for line in io::BufReader::new(file).lines() {
let line = line?;
let t = line.trim();
if t.is_empty() || t.starts_with('#') { continue; }
if let Some((k, v)) = t.split_once('=') {
map.insert(k.trim().to_string(), v.trim().to_string());
}
}
Ok(map)
}
pub fn get(&self, key: &str) -> Option<&str> {
self.data.get(key).map(String::as_str)
}
pub fn get_or<'a>(&'a self, key: &str, default: &'a str) -> &'a str {
self.get(key).unwrap_or(default)
}
pub fn get_int(&self, key: &str) -> Option<i64> {
self.get(key)?.trim().parse().ok()
}
pub fn get_int_or(&self, key: &str, default: i64) -> i64 {
self.get_int(key).unwrap_or(default)
}
pub fn get_float(&self, key: &str) -> Option<f64> {
self.get(key)?.trim().parse().ok()
}
pub fn get_float_or(&self, key: &str, default: f64) -> f64 {
self.get_float(key).unwrap_or(default)
}
pub fn get_bool(&self, key: &str) -> Option<bool> {
match self.get(key)?.trim().to_lowercase().as_str() {
"true" | "yes" | "1" | "on" => Some(true),
"false"| "no" | "0" | "off" => Some(false),
_ => None,
}
}
pub fn get_bool_or(&self, key: &str, default: bool) -> bool {
self.get_bool(key).unwrap_or(default)
}
pub fn set(&mut self, key: impl Into<String>, value: impl ToString) {
self.data.insert(key.into(), value.to_string());
}
pub fn remove(&mut self, key: &str) -> Option<String> {
self.data.remove(key)
}
pub fn contains(&self, key: &str) -> bool {
self.data.contains_key(key)
}
pub fn save(&self) -> io::Result<()> {
if let Some(p) = self.path.parent() {
std::fs::create_dir_all(p)?;
}
let mut file = std::fs::File::create(&self.path)?;
writeln!(file, "# Yog mod configuration — auto-generated")?;
let mut keys: Vec<_> = self.data.keys().collect();
keys.sort();
for k in keys {
writeln!(file, "{} = {}", k, self.data[k])?;
}
Ok(())
}
pub fn save_defaults(&self) -> io::Result<()> {
if !self.path.exists() { self.save() } else { Ok(()) }
}
pub fn len(&self) -> usize { self.data.len() }
pub fn is_empty(&self) -> bool { self.data.is_empty() }
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.data.iter().map(|(k, v)| (k.as_str(), v.as_str()))
}
}