use crate::types::DynError;
use async_trait::async_trait;
use serde::de::DeserializeOwned;
use std::ffi::OsStr;
use std::fmt::Debug;
use std::path::{Component, Path, PathBuf};
use tracing::error;
use super::ConfigLoader;
pub const DEFAULT_HOME_DIR: &str = ".pubky-nexus";
const DEFAULT_CONFIG_TOML: &str = include_str!("../../../default.config.toml");
pub const CONFIG_FILE_NAME: &str = "config.toml";
pub fn expand_home_dir(path: PathBuf) -> PathBuf {
if let Some(first) = path.components().next() {
if first == Component::Normal(OsStr::new("~")) {
if let Some(home) = dirs::home_dir() {
let without_tilde = path.iter().skip(1).collect::<PathBuf>();
return home.join(without_tilde);
}
}
}
path
}
#[async_trait]
pub trait ConfigReader<T>: ConfigLoader<T>
where
T: DeserializeOwned + Send + Sync + Debug,
{
fn get_config_file_path(expanded_path: &Path) -> PathBuf {
expanded_path.join(CONFIG_FILE_NAME)
}
fn write_default_config_file(config_file_path: &PathBuf) -> std::io::Result<()> {
if let Some(parent) = config_file_path.parent() {
println!(
"Validating existence of '{}' and creating it if missing before copying '{CONFIG_FILE_NAME}' file…",
parent.display()
);
std::fs::create_dir_all(parent)?;
}
std::fs::write(config_file_path, DEFAULT_CONFIG_TOML)?;
Ok(())
}
async fn read_config_file(expanded_path: PathBuf) -> Result<T, DynError> {
let config_file_path = Self::get_config_file_path(&expanded_path);
if !config_file_path.exists() {
Self::write_default_config_file(&config_file_path)?;
}
println!(
"nexusd reading the '{CONFIG_FILE_NAME}' file from '{}'",
expanded_path.display()
);
let config = <Self as ConfigLoader<T>>::load(config_file_path)
.await
.map_err(|e| {
error!(
"Failed to load config file {:?}: {}",
Self::get_config_file_path(&expanded_path),
e
);
e
})?;
Ok(config)
}
}
#[async_trait]
impl<T> ConfigReader<T> for T where T: ConfigLoader<T> + DeserializeOwned + Send + Sync + Debug {}