1use crate::error::CliError;
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5#[derive(Debug, Serialize, Deserialize, Default)]
6pub struct Config {
7 pub auth: AuthConfig,
8 #[serde(default)]
9 pub defaults: DefaultsConfig,
10 #[serde(default)]
11 pub git: GitConfig,
12}
13
14#[derive(Debug, Serialize, Deserialize, Default)]
15pub struct AuthConfig {
16 pub token: String,
17}
18
19#[derive(Debug, Serialize, Deserialize, Default)]
20pub struct DefaultsConfig {
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub workspace_id: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub output: Option<String>,
25}
26
27#[derive(Debug, Serialize, Deserialize, Default)]
28pub struct GitConfig {
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub enabled: Option<bool>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub verbose: Option<bool>,
33}
34
35impl Config {
36 pub fn config_path() -> Result<PathBuf, CliError> {
37 let config_dir = dirs::config_dir()
38 .ok_or_else(|| CliError::ConfigError("Could not determine config directory".into()))?;
39 Ok(config_dir.join("clickup-cli").join("config.toml"))
40 }
41
42 pub fn load() -> Result<Self, CliError> {
44 let project_path = PathBuf::from(".clickup.toml");
46 if project_path.exists() {
47 let project_config = Self::load_from(&project_path)?;
48 if !project_config.auth.token.is_empty() {
49 return Ok(project_config);
50 }
51 }
52 let path = Self::config_path()?;
54 Self::load_from(&path)
55 }
56
57 pub fn load_from(path: &std::path::Path) -> Result<Self, CliError> {
58 if !path.exists() {
59 return Err(CliError::ConfigError("Not configured".into()));
60 }
61 let contents = std::fs::read_to_string(path)?;
62 toml::from_str(&contents)
63 .map_err(|e| CliError::ConfigError(format!("Invalid config file: {}", e)))
64 }
65
66 pub fn save(&self) -> Result<(), CliError> {
67 let path = Self::config_path()?;
68 self.save_to(&path)
69 }
70
71 pub fn save_to(&self, path: &std::path::Path) -> Result<(), CliError> {
72 if let Some(parent) = path.parent() {
73 std::fs::create_dir_all(parent)?;
74 }
75 let contents = toml::to_string_pretty(self)
76 .map_err(|e| CliError::ConfigError(format!("Failed to serialize config: {}", e)))?;
77 std::fs::write(path, contents)?;
78 Ok(())
79 }
80}