Skip to main content

kimun_notes/settings/
workspace_config.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::path::PathBuf;
5
6#[derive(Debug, Clone)]
7pub enum WorkspaceConfigError {
8    DuplicateWorkspace {
9        name: String,
10        existing_path: PathBuf,
11    },
12}
13
14impl std::fmt::Display for WorkspaceConfigError {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match self {
17            WorkspaceConfigError::DuplicateWorkspace { name, existing_path } => {
18                write!(f, "Workspace '{}' already exists at {:?}", name, existing_path)
19            }
20        }
21    }
22}
23
24impl std::error::Error for WorkspaceConfigError {}
25
26#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
27pub struct GlobalConfig {
28    pub current_workspace: String,
29    pub theme: String,
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
33pub struct WorkspaceEntry {
34    pub path: PathBuf,
35    pub last_paths: Vec<String>,
36    pub created: DateTime<Utc>,
37    #[serde(default)]
38    pub quick_note_path: Option<String>,
39}
40
41impl WorkspaceEntry {
42    pub fn effective_quick_note_path(&self) -> String {
43        self.quick_note_path.clone().unwrap_or_else(|| kimun_core::nfs::VaultPath::root().to_string())
44    }
45}
46
47#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
48pub struct WorkspaceConfig {
49    pub global: GlobalConfig,
50    pub workspaces: HashMap<String, WorkspaceEntry>,
51}
52
53impl WorkspaceConfig {
54    pub fn new_empty() -> Self {
55        Self {
56            global: GlobalConfig {
57                current_workspace: String::new(),
58                theme: "dark".to_string(),
59            },
60            workspaces: HashMap::new(),
61        }
62    }
63
64    pub fn add_workspace(&mut self, name: String, path: PathBuf) -> Result<(), WorkspaceConfigError> {
65        if self.workspaces.contains_key(&name) {
66            return Err(WorkspaceConfigError::DuplicateWorkspace {
67                name: name.clone(),
68                existing_path: self.workspaces[&name].path.clone(),
69            });
70        }
71
72        let entry = WorkspaceEntry {
73            path,
74            last_paths: Vec::new(),
75            created: Utc::now(),
76            quick_note_path: None,
77        };
78
79        self.workspaces.insert(name.clone(), entry);
80
81        // Set as current if it's the first workspace
82        if self.workspaces.len() == 1 {
83            self.global.current_workspace = name.clone();
84        }
85
86        Ok(())
87    }
88
89    pub fn get_current_workspace(&self) -> Option<&WorkspaceEntry> {
90        self.workspaces.get(&self.global.current_workspace)
91    }
92
93    pub fn get_workspace(&self, name: &str) -> Option<&WorkspaceEntry> {
94        self.workspaces.get(name)
95    }
96
97    pub fn from_phase1_migration(
98        workspace_dir: PathBuf,
99        theme: String,
100        last_paths: Vec<String>,
101    ) -> Self {
102        let mut config = Self::new_empty();
103        config.global.theme = theme;
104
105        let entry = WorkspaceEntry {
106            path: workspace_dir,
107            last_paths,
108            created: Utc::now(),
109            quick_note_path: None,
110        };
111
112        config.workspaces.insert("default".to_string(), entry);
113        config.global.current_workspace = "default".to_string();
114
115        config
116    }
117}