1use std::fs; 2use std::path::{Path, PathBuf}; 3 4use anyhow::{Context, Result}; 5use dirs::config_dir; 6use serde::{Deserialize, Serialize}; 7 8#[derive(Debug, Clone, Serialize, Deserialize, Default)] 9pub struct RootConfig { 10 #[serde(default)] 11 pub default: DefaultSection, 12 #[serde(default)] 13 pub auth: AuthSection, 14 #[serde(default)] 15 pub knots: KnotsSection, 16 #[serde(default)] 17 pub ui: UiSection, 18} 19 20#[derive(Debug, Clone, Serialize, Deserialize, Default)] 21pub struct DefaultSection { 22 pub knot: Option<String>, 23 pub editor: Option<String>, 24 pub pager: Option<String>, 25 #[serde(default = "default_format")] 26 pub format: String, 27} 28 29fn default_format() -> String { 30 "table".to_string() 31} 32 33#[derive(Debug, Clone, Serialize, Deserialize, Default)] 34pub struct AuthSection { 35 pub handle: Option<String>, 36 pub did: Option<String>, 37 pub pds_url: Option<String>, 38} 39 40#[derive(Debug, Clone, Serialize, Deserialize, Default)] 41pub struct KnotsSection { 42 pub default: Option<String>, 43 #[serde(default)] 44 pub custom: serde_json::Value, 45} 46 47#[derive(Debug, Clone, Serialize, Deserialize, Default)] 48pub struct UiSection { 49 #[serde(default)] 50 pub color: bool, 51 #[serde(default)] 52 pub progress_bar: bool, 53 #[serde(default)] 54 pub confirm_destructive: bool, 55} 56 57pub fn default_config_path() -> Result<PathBuf> { 58 let base = config_dir().context("Could not determine platform config directory")?; 59 Ok(base.join("tangled").join("config.toml")) 60} 61 62pub fn load_config(path: Option<&Path>) -> Result<Option<RootConfig>> { 63 let path = path 64 .map(|p| p.to_path_buf()) 65 .unwrap_or(default_config_path()?); 66 if !path.exists() { 67 return Ok(None); 68 } 69 let content = fs::read_to_string(&path) 70 .with_context(|| format!("Failed reading config file: {}", path.display()))?; 71 let cfg: RootConfig = toml::from_str(&content).context("Invalid TOML in config")?; 72 Ok(Some(cfg)) 73} 74 75pub fn save_config(cfg: &RootConfig, path: Option<&Path>) -> Result<()> { 76 let path = path 77 .map(|p| p.to_path_buf()) 78 .unwrap_or(default_config_path()?); 79 if let Some(parent) = path.parent() { 80 std::fs::create_dir_all(parent)?; 81 } 82 let toml = toml::to_string_pretty(cfg)?; 83 fs::write(&path, toml) 84 .with_context(|| format!("Failed writing config file: {}", path.display()))?; 85 Ok(()) 86}