use std::net::SocketAddr;
use std::path::PathBuf;
use serde::Deserialize;
const CONFIG_FILE_NAME: &str = "mail4agent.toml";
const DEFAULT_BIND: &str = "127.0.0.1:18301";
const DB_PATH_ENV: &str = "MAIL4AGENT_DB_PATH";
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct Config {
pub bind: SocketAddr,
pub db_path: Option<PathBuf>,
}
impl Default for Config {
fn default() -> Self {
Self {
bind: DEFAULT_BIND
.parse()
.expect("DEFAULT_BIND is a valid socket address literal"),
db_path: None,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("read {0}: {1}")]
Read(PathBuf, std::io::Error),
#[error("parse {0}: {1}")]
Parse(PathBuf, Box<toml::de::Error>),
#[error(
"no home/data directory available to place the default mailbox database -- \
set {DB_PATH_ENV} explicitly"
)]
NoDataDir,
}
impl Config {
pub fn load() -> Result<Self, ConfigError> {
let path = PathBuf::from(CONFIG_FILE_NAME);
if !path.exists() {
return Ok(Self::default());
}
let raw = std::fs::read_to_string(&path).map_err(|err| ConfigError::Read(path.clone(), err))?;
toml::from_str(&raw).map_err(|err| ConfigError::Parse(path, Box::new(err)))
}
pub fn resolve_db_path(&self) -> Result<PathBuf, ConfigError> {
if let Ok(from_env) = std::env::var(DB_PATH_ENV) {
return Ok(PathBuf::from(from_env));
}
if let Some(path) = &self.db_path {
return Ok(path.clone());
}
let dirs = directories::ProjectDirs::from("", "", "mail4agent").ok_or(ConfigError::NoDataDir)?;
Ok(dirs.data_dir().join("mail4agent.sqlite"))
}
}