use std::{env, fs, path::PathBuf};
use anyhow::{Context, Result};
use directories::ProjectDirs;
use serde::Deserialize;
pub const DEFAULT_BASE: &str = "https://zero.ski";
pub const USER_AGENT: &str = concat!("zeroski/", env!("CARGO_PKG_VERSION"));
#[derive(Debug, Clone)]
pub struct Config {
pub base: String,
pub key: Option<String>,
}
#[derive(Debug, Default, Deserialize)]
struct FileConfig {
base: Option<String>,
key: Option<String>,
}
impl Config {
pub fn resolve(base_flag: Option<String>, key_flag: Option<String>) -> Result<Self> {
let file = load_file_config().unwrap_or_default();
let base = base_flag
.or_else(|| env::var("ZEROSKI_BASE").ok())
.or(file.base)
.unwrap_or_else(|| DEFAULT_BASE.to_string())
.trim_end_matches('/')
.to_string();
let key = key_flag
.or_else(|| env::var("ZEROSKI_KEY").ok())
.or(file.key);
Ok(Self { base, key })
}
pub fn config_path() -> Option<PathBuf> {
ProjectDirs::from("ski", "zero", "zeroski")
.map(|p| p.config_dir().join("config.toml"))
}
}
fn load_file_config() -> Result<FileConfig> {
let Some(path) = Config::config_path() else {
return Ok(FileConfig::default());
};
if !path.exists() {
return Ok(FileConfig::default());
}
let text = fs::read_to_string(&path)
.with_context(|| format!("reading {}", path.display()))?;
let parsed: FileConfig = toml::from_str(&text)
.with_context(|| format!("parsing {}", path.display()))?;
Ok(parsed)
}