kimun_notes/cli/
helpers.rs1use crate::settings::AppSettings;
6use color_eyre::eyre::Result;
7use kimun_core::nfs::{PATH_SEPARATOR, VaultPath};
8use kimun_core::{NoteVault, VaultConfig};
9use std::path::PathBuf;
10
11pub fn load_settings(config_path: Option<PathBuf>) -> Result<AppSettings> {
13 match config_path {
14 Some(path) => AppSettings::load_from_file(path),
15 None => AppSettings::load_from_disk(),
16 }
17}
18
19pub fn resolve_workspace_config(settings: &AppSettings) -> Result<(PathBuf, String)> {
23 let path = settings.resolve_workspace_path();
24 let name = settings
25 .workspace_config
26 .as_ref()
27 .map(|wc| wc.global.current_workspace.clone())
28 .unwrap_or_else(|| "default".to_string());
29
30 match path {
31 Some(p) => Ok((p, name)),
32 None => Err(color_eyre::eyre::eyre!(
33 "No workspace configured. Run 'kimun' to set up a workspace."
34 )),
35 }
36}
37
38pub fn load_and_resolve_workspace(
43 config_path: Option<PathBuf>,
44) -> Result<(AppSettings, PathBuf, String)> {
45 let settings = load_settings(config_path)?;
46 let (workspace_path, workspace_name) = resolve_workspace_config(&settings)?;
47 Ok((settings, workspace_path, workspace_name))
48}
49
50pub fn resolve_quick_note_path(settings: &AppSettings) -> String {
53 let root = kimun_core::nfs::VaultPath::root().to_string();
54 if settings.workspace_dir.is_some() {
56 return root;
57 }
58 if let Some(ref ws_config) = settings.workspace_config
60 && let Some(entry) = ws_config.get_current_workspace()
61 {
62 return entry.effective_quick_note_path();
63 }
64 root
65}
66
67pub fn resolve_inbox_path(settings: &AppSettings) -> String {
69 if let Some(ref wc) = settings.workspace_config
70 && let Some(entry) = wc.get_current_workspace()
71 {
72 return entry.effective_inbox_path();
73 }
74 kimun_core::DEFAULT_INBOX_PATH.to_string()
75}
76
77pub fn resolve_note_path(input: &str, quick_note_path: &str) -> Result<VaultPath> {
85 let trimmed = input.trim();
86 if trimmed.is_empty() {
87 return Err(color_eyre::eyre::eyre!(
88 "Note path cannot be empty or whitespace-only"
89 ));
90 }
91 if trimmed.len() == 1 && trimmed.starts_with(PATH_SEPARATOR) {
92 return Err(color_eyre::eyre::eyre!(
93 "Note path cannot be the root separator alone"
94 ));
95 }
96 let raw = if trimmed.starts_with(PATH_SEPARATOR) {
97 trimmed.to_string()
98 } else {
99 let base = if quick_note_path.trim().is_empty() {
100 VaultPath::root().to_string()
101 } else {
102 quick_note_path.trim_end_matches(PATH_SEPARATOR).to_string()
103 };
104 format!("{}{}{}", base, PATH_SEPARATOR, trimmed)
105 };
106 Ok(VaultPath::note_path_from(&raw))
107}
108
109pub fn resolve_content(content: Option<String>) -> color_eyre::eyre::Result<String> {
113 use std::io::IsTerminal;
114 match content {
115 Some(c) => Ok(c),
116 None => {
117 if std::io::stdin().is_terminal() {
118 Ok(String::new())
119 } else {
120 use std::io::Read;
121 let mut buf = String::new();
122 std::io::stdin()
123 .read_to_string(&mut buf)
124 .map_err(|e| color_eyre::eyre::eyre!("Failed to read stdin: {}", e))?;
125 Ok(buf.trim_end_matches(['\n', '\r']).to_string())
126 }
127 }
128 }
129}
130
131pub async fn create_and_init_vault(config_path: Option<PathBuf>) -> Result<(NoteVault, String)> {
136 let (settings, workspace_path, workspace_name) = load_and_resolve_workspace(config_path)?;
137
138 let cache_path = settings.cache_path_for(&workspace_name);
139 let mut vault =
140 NoteVault::new(VaultConfig::new(&workspace_path).with_db_path(cache_path)).await?;
141 let inbox = resolve_inbox_path(&settings);
142 vault.set_inbox_path(kimun_core::nfs::VaultPath::new(&inbox));
143 vault.validate_and_init().await?;
144
145 Ok((vault, workspace_name))
146}