use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ConfigFile {
#[serde(default)]
pub default: Profile,
#[serde(default)]
pub profiles: BTreeMap<String, Profile>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Profile {
#[serde(default)]
pub endpoint: Option<String>,
#[serde(default)]
pub identity: Option<PathBuf>,
#[serde(default)]
pub netdb: Option<PathBuf>,
#[serde(default)]
pub default_timeout_ms: Option<u64>,
#[serde(default)]
pub ice_signature_threshold: Option<usize>,
}
impl ConfigFile {
pub fn profile(&self, name: &str) -> Profile {
if name == "default" {
return self.default.clone();
}
self.profiles.get(name).cloned().unwrap_or_default()
}
pub async fn load(path: Option<&Path>) -> Result<Self, ConfigError> {
let path = match path {
Some(p) => p.to_path_buf(),
None => match default_path() {
Some(p) => p,
None => return Ok(Self::default()),
},
};
match tokio::fs::read_to_string(&path).await {
Ok(s) => toml::from_str(&s).map_err(|e| ConfigError::Parse {
path: path.clone(),
source: e,
}),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(Self::default()),
Err(e) => Err(ConfigError::Io {
path: path.clone(),
source: e,
}),
}
}
}
pub fn default_path() -> Option<PathBuf> {
dirs::config_dir().map(|d| d.join("net-mesh").join("config.toml"))
}
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("config file at {path}: {source}")]
Io {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("config file at {path} failed to parse: {source}")]
Parse {
path: PathBuf,
#[source]
source: toml::de::Error,
},
}