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}