use std::fs;
use std::fmt;
use std::error::Error;
use yaml_rust2::{YamlLoader, Yaml};
mod defaults {
pub fn cache_expiration_time() -> u64 { 150 }
pub fn argon_salt() -> String { String::from("0123456789012345") }
pub fn hkdf_salt() -> String { String::from("012345678901234") }
pub fn lock_token() -> String { String::from("5up3r5ubtl353cr3t") }
}
#[derive(Debug)]
pub struct Config {
pub cache_expiration_time: u64,
pub argon_salt: String,
pub hkdf_salt: String,
pub lock_token: String,
pub spmg_path: String
}
#[derive(Debug)]
pub enum ConfigError {
Io(std::io::Error),
Yaml(yaml_rust2::ScanError),
Structure(String),
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConfigError::Io(e) => write!(f, "I/O error: {}", e),
ConfigError::Yaml(e) => write!(f, "YAML parse error: {}", e),
ConfigError::Structure(msg) => write!(f, "YAML structure error: {}", msg),
}
}
}
impl Error for ConfigError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
ConfigError::Io(e) => Some(e),
ConfigError::Yaml(e) => Some(e),
ConfigError::Structure(_) => None,
}
}
}
impl From<std::io::Error> for ConfigError {
fn from(err: std::io::Error) -> ConfigError {
ConfigError::Io(err)
}
}
impl From<yaml_rust2::ScanError> for ConfigError {
fn from(err: yaml_rust2::ScanError) -> ConfigError {
ConfigError::Yaml(err)
}
}
fn load_config(spmg_path: &str) -> Result<Config, ConfigError> {
let path = format!("{}/config.yaml", spmg_path);
let content = fs::read_to_string(path)?;
let docs = YamlLoader::load_from_str(&content)?;
let doc = docs.get(0).ok_or_else(|| ConfigError::Structure("Empty YAML document".into()))?;
let cache_expiration_time = get_u64(doc, "cache_expiration_time")
.unwrap_or_else(defaults::cache_expiration_time);
let argon_salt = get_string(doc, "argon_salt")
.unwrap_or_else(defaults::argon_salt);
let hkdf_salt = get_string(doc, "hkdf_salt")
.unwrap_or_else(defaults::hkdf_salt);
let lock_token = get_string(doc, "lock_token")
.unwrap_or_else(defaults::lock_token);
Ok(Config {
cache_expiration_time,
argon_salt,
hkdf_salt,
lock_token,
spmg_path: spmg_path.to_string(),
})
}
fn get_u64(doc: &Yaml, key: &str) -> Option<u64> {
doc[key].as_i64().and_then(|v| if v >= 0 { Some(v as u64) } else { None })
}
fn get_string(doc: &Yaml, key: &str) -> Option<String> {
doc[key].as_str().map(|s| s.to_string())
}
impl Config {
pub fn new(spmg_path: String) -> Self {
match load_config(&spmg_path) {
Ok(cfg) => {
cfg
}
Err(e) => {
eprintln!("Failed to load config: {}. Falling back to default.", e);
Config {
cache_expiration_time: defaults::cache_expiration_time(),
argon_salt: defaults::argon_salt(),
hkdf_salt: defaults::hkdf_salt(),
lock_token: defaults::lock_token(),
spmg_path: spmg_path,
}
}
}
}
}