Skip to main content

dkdc_config/
lib.rs

1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::path::{Path, PathBuf};
5
6/// Configuration file structure
7#[derive(Debug, Clone, Deserialize, Serialize, Default)]
8pub struct ConfigFile {
9    #[serde(default)]
10    pub general: GeneralConfig,
11    #[serde(default)]
12    pub dev: DevConfig,
13}
14
15#[derive(Debug, Clone, Deserialize, Serialize)]
16pub struct GeneralConfig {
17    #[serde(default = "default_editor")]
18    pub editor: String,
19}
20
21impl Default for GeneralConfig {
22    fn default() -> Self {
23        Self {
24            editor: default_editor(),
25        }
26    }
27}
28
29fn default_editor() -> String {
30    std::env::var("EDITOR").unwrap_or_else(|_| "nano".to_string())
31}
32
33#[derive(Debug, Clone, Deserialize, Serialize, Default)]
34pub struct DevConfig {
35    #[serde(default)]
36    pub packages: Vec<String>,
37}
38
39#[derive(Clone)]
40pub struct Config {
41    dkdc_dir: PathBuf,
42    config_file: Option<ConfigFile>,
43}
44
45impl Config {
46    pub fn new() -> Result<Self> {
47        let home = std::env::var("HOME").or_else(|_| std::env::var("USERPROFILE"))?;
48        let dkdc_dir = PathBuf::from(home).join(".dkdc");
49        let mut config = Self {
50            dkdc_dir,
51            config_file: None,
52        };
53
54        // Try to load config file
55        config.reload_config_file();
56
57        Ok(config)
58    }
59
60    pub fn from_path(dkdc_dir: PathBuf) -> Self {
61        let mut config = Self {
62            dkdc_dir,
63            config_file: None,
64        };
65        config.reload_config_file();
66        config
67    }
68
69    /// Reload configuration file from disk
70    pub fn reload_config_file(&mut self) {
71        let config_path = self.config_file_path();
72        if config_path.exists() {
73            match fs::read_to_string(&config_path) {
74                Ok(content) => match toml::from_str(&content) {
75                    Ok(config) => self.config_file = Some(config),
76                    Err(_) => self.config_file = Some(ConfigFile::default()),
77                },
78                Err(_) => self.config_file = Some(ConfigFile::default()),
79            }
80        }
81    }
82
83    /// Get the loaded config file (or default)
84    pub fn file(&self) -> ConfigFile {
85        self.config_file.clone().unwrap_or_default()
86    }
87
88    pub fn dkdc_dir(&self) -> &Path {
89        &self.dkdc_dir
90    }
91
92    pub fn lake_dir(&self) -> PathBuf {
93        self.dkdc_dir.join("dkdclake")
94    }
95
96    pub fn metadata_path(&self) -> PathBuf {
97        self.lake_dir().join("metadata.db")
98    }
99
100    pub fn data_path(&self) -> PathBuf {
101        self.lake_dir().join("data")
102    }
103
104    pub fn venv_path(&self) -> PathBuf {
105        self.dkdc_dir.join("venv")
106    }
107
108    pub fn python_path(&self) -> PathBuf {
109        let venv = self.venv_path();
110        if cfg!(windows) {
111            venv.join("Scripts").join("python.exe")
112        } else {
113            venv.join("bin").join("python")
114        }
115    }
116
117    pub fn config_file_path(&self) -> PathBuf {
118        let config_dir = if let Ok(xdg_config) = std::env::var("XDG_CONFIG_HOME") {
119            PathBuf::from(xdg_config)
120        } else if let Ok(home) = std::env::var("HOME") {
121            PathBuf::from(home).join(".config")
122        } else {
123            self.dkdc_dir.clone()
124        };
125
126        config_dir.join("dkdc").join("config.toml")
127    }
128
129    pub fn ensure_directories(&self) -> Result<()> {
130        fs::create_dir_all(&self.dkdc_dir)?;
131        fs::create_dir_all(self.lake_dir())?;
132        fs::create_dir_all(self.data_path())?;
133        Ok(())
134    }
135
136    pub fn ensure_metadata_db(&self) -> Result<()> {
137        self.ensure_directories()?;
138
139        if !self.metadata_path().exists() {
140            fs::File::create(self.metadata_path())?;
141        }
142
143        Ok(())
144    }
145}
146
147pub const SECRETS_TABLE_NAME: &str = "secrets";
148pub const FILES_TABLE_NAME: &str = "files";
149pub const ARCHIVES_TABLE_NAME: &str = "archives";
150
151pub const DUCKLAKE_EXTENSION: &str = "ducklake";
152pub const SQLITE_EXTENSION: &str = "sqlite";
153
154pub const ARCHIVE_FILENAME_TEMPLATE: &str = "archive_directory_{name}.zip";
155
156pub struct Colors;
157
158impl Colors {
159    pub const PRIMARY: &'static str = "#8b5cf6";
160    pub const SECONDARY: &'static str = "#06b6d4";
161    pub const SUCCESS: &'static str = "#10b981";
162    pub const WARNING: &'static str = "#f59e0b";
163    pub const ERROR: &'static str = "#ef4444";
164    pub const MUTED: &'static str = "#6b7280";
165    pub const ACCENT: &'static str = "#a855f7";
166}
167
168pub const DKDC_BANNER: &str = r#"
169▓█████▄  ██ ▄█▀▓█████▄  ▄████▄  
170▒██▀ ██▌ ██▄█▒ ▒██▀ ██▌▒██▀ ▀█  
171░██   █▌▓███▄░ ░██   █▌▒▓█    ▄ 
172░▓█▄   ▌▓██ █▄ ░▓█▄   ▌▒▓▓▄ ▄██▒
173░▒████▓ ▒██▒ █▄░▒████▓ ▒ ▓███▀ ░
174 ▒▒▓  ▒ ▒ ▒▒ ▓▒ ▒▒▓  ▒ ░ ░▒ ▒  ░
175 ░ ▒  ▒ ░ ░▒ ▒░ ░ ▒  ▒   ░  ▒   
176 ░ ░  ░ ░ ░░ ░  ░ ░  ░ ░        
177   ░    ░  ░      ░    ░ ░      
178 ░              ░      ░
179
180develop knowledge, develop code
181"#;
182
183#[allow(dead_code)]
184pub const DKDC_BANNER_V1: &str = r#"
185██████╗ ██╗  ██╗██████╗  ██████╗
186██╔══██╗██║ ██╔╝██╔══██╗██╔════╝
187██║  ██║█████╔╝ ██║  ██║██║     
188██║  ██║██╔═██╗ ██║  ██║██║     
189██████╔╝██║  ██╗██████╔╝╚██████╗
190╚═════╝ ╚═╝  ╚═╝╚═════╝  ╚═════╝
191
192develop knowledge, develop code
193"#;
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_config_creation() {
201        let config = Config::new();
202        assert!(config.is_ok());
203    }
204
205    #[test]
206    fn test_paths() {
207        let config = Config::from_path(PathBuf::from("/home/test/.dkdc"));
208        assert_eq!(config.dkdc_dir().display().to_string(), "/home/test/.dkdc");
209        assert_eq!(
210            config.lake_dir().display().to_string(),
211            "/home/test/.dkdc/dkdclake"
212        );
213        assert_eq!(
214            config.metadata_path().display().to_string(),
215            "/home/test/.dkdc/dkdclake/metadata.db"
216        );
217    }
218}