1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(default)]
7pub struct Config {
8 pub editor: EditorConfig,
9 pub open: OpenConfig,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(default)]
14pub struct EditorConfig {
15 pub command: Option<String>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(default)]
21pub struct OpenConfig {
22 pub editor: bool,
23}
24
25impl Default for Config {
26 fn default() -> Self {
27 Self {
28 editor: EditorConfig::default(),
29 open: OpenConfig::default(),
30 }
31 }
32}
33
34impl Default for EditorConfig {
35 fn default() -> Self {
36 Self { command: None }
37 }
38}
39
40impl Default for OpenConfig {
41 fn default() -> Self {
42 Self { editor: true }
43 }
44}
45
46impl Config {
47 pub fn path() -> Result<PathBuf> {
48 let config_dir = dirs::config_dir()
49 .context("Could not determine config directory")?;
50 Ok(config_dir.join("worktree").join("config.toml"))
51 }
52
53 pub fn load() -> Result<Self> {
54 let path = Self::path()?;
55 if !path.exists() {
56 return Ok(Self::default());
57 }
58 let content = std::fs::read_to_string(&path)
59 .with_context(|| format!("Failed to read config from {}", path.display()))?;
60 let config: Self = toml::from_str(&content)
61 .with_context(|| format!("Failed to parse config at {}", path.display()))?;
62 Ok(config)
63 }
64
65 pub fn save(&self) -> Result<()> {
66 let path = Self::path()?;
67 if let Some(parent) = path.parent() {
68 std::fs::create_dir_all(parent)
69 .with_context(|| format!("Failed to create config dir {}", parent.display()))?;
70 }
71 let content = toml::to_string_pretty(self)
72 .context("Failed to serialize config")?;
73 std::fs::write(&path, content)
74 .with_context(|| format!("Failed to write config to {}", path.display()))?;
75 Ok(())
76 }
77
78 pub fn get_value(&self, key: &str) -> Result<String> {
80 match key {
81 "editor.command" => Ok(self.editor.command.clone().unwrap_or_default()),
82 "open.editor" => Ok(self.open.editor.to_string()),
83 _ => anyhow::bail!("Unknown config key: {key}"),
84 }
85 }
86
87 pub fn set_value(&mut self, key: &str, value: &str) -> Result<()> {
89 match key {
90 "editor.command" => {
91 self.editor.command = if value.is_empty() { None } else { Some(value.to_string()) };
92 }
93 "open.editor" => {
94 self.open.editor = value.parse::<bool>()
95 .with_context(|| format!("Invalid boolean value: {value}"))?;
96 }
97 _ => anyhow::bail!("Unknown config key: {key}"),
98 }
99 Ok(())
100 }
101}