kimun_notes/settings/
workspace_config.rs1use 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 {
18 name,
19 existing_path,
20 } => {
21 write!(
22 f,
23 "Workspace '{}' already exists at {:?}",
24 name, existing_path
25 )
26 }
27 }
28 }
29}
30
31impl std::error::Error for WorkspaceConfigError {}
32
33#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
34pub struct GlobalConfig {
35 pub current_workspace: String,
36}
37
38#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
39pub struct WorkspaceEntry {
40 pub path: PathBuf,
41 pub last_paths: Vec<String>,
42 pub created: DateTime<Utc>,
43 #[serde(default)]
44 pub quick_note_path: Option<String>,
45 #[serde(default)]
46 pub inbox_path: Option<String>,
47 #[serde(skip)]
50 pub resolved_path: Option<PathBuf>,
51}
52
53impl WorkspaceEntry {
54 pub fn effective_path(&self) -> &PathBuf {
56 self.resolved_path.as_ref().unwrap_or(&self.path)
57 }
58
59 pub fn effective_quick_note_path(&self) -> String {
60 self.quick_note_path
61 .clone()
62 .unwrap_or_else(|| kimun_core::nfs::VaultPath::root().to_string())
63 }
64
65 pub fn effective_inbox_path(&self) -> String {
66 self.inbox_path
67 .clone()
68 .unwrap_or_else(|| kimun_core::DEFAULT_INBOX_PATH.to_string())
69 }
70}
71
72#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
73pub struct WorkspaceConfig {
74 pub global: GlobalConfig,
75 pub workspaces: HashMap<String, WorkspaceEntry>,
76}
77
78impl WorkspaceConfig {
79 pub fn new_empty() -> Self {
80 Self {
81 global: GlobalConfig {
82 current_workspace: String::new(),
83 },
84 workspaces: HashMap::new(),
85 }
86 }
87
88 pub fn add_workspace(
89 &mut self,
90 name: String,
91 path: PathBuf,
92 ) -> Result<(), WorkspaceConfigError> {
93 if self.workspaces.contains_key(&name) {
94 return Err(WorkspaceConfigError::DuplicateWorkspace {
95 name: name.clone(),
96 existing_path: self.workspaces[&name].path.clone(),
97 });
98 }
99
100 let entry = WorkspaceEntry {
101 path,
102 last_paths: Vec::new(),
103 created: Utc::now(),
104 quick_note_path: None,
105 inbox_path: None,
106 resolved_path: None,
107 };
108
109 self.workspaces.insert(name.clone(), entry);
110
111 if self.workspaces.len() == 1 {
113 self.global.current_workspace = name.clone();
114 }
115
116 Ok(())
117 }
118
119 pub fn get_current_workspace(&self) -> Option<&WorkspaceEntry> {
120 self.workspaces.get(&self.global.current_workspace)
121 }
122
123 pub fn get_workspace(&self, name: &str) -> Option<&WorkspaceEntry> {
124 self.workspaces.get(name)
125 }
126
127 pub fn from_phase1_migration(workspace_dir: PathBuf, last_paths: Vec<String>) -> Self {
128 let mut config = Self::new_empty();
129
130 let entry = WorkspaceEntry {
131 path: workspace_dir,
132 last_paths,
133 created: Utc::now(),
134 quick_note_path: None,
135 inbox_path: None,
136 resolved_path: None,
137 };
138
139 config.workspaces.insert("default".to_string(), entry);
140 config.global.current_workspace = "default".to_string();
141
142 config
143 }
144}