kimun_notes/cli/
helpers.rs1use crate::settings::AppSettings;
6use color_eyre::eyre::Result;
7use kimun_core::nfs::{PATH_SEPARATOR, VaultPath};
8use kimun_core::{NoteVault, SystemPath, VaultConfig};
9use std::path::PathBuf;
10
11pub fn load_settings(config_path: Option<PathBuf>) -> Result<AppSettings> {
13 Ok(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<(SystemPath, 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, SystemPath, 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 let Some(ref ws_config) = settings.workspace_config
55 && let Some(entry) = ws_config.get_current_workspace()
56 {
57 return entry.effective_quick_note_path();
58 }
59 root
60}
61
62pub fn resolve_inbox_path(settings: &AppSettings) -> String {
64 if let Some(ref wc) = settings.workspace_config
65 && let Some(entry) = wc.get_current_workspace()
66 {
67 return entry.effective_inbox_path();
68 }
69 kimun_core::DEFAULT_INBOX_PATH.to_string()
70}
71
72pub fn resolve_note_path(input: &str, quick_note_path: &str) -> Result<VaultPath> {
80 let trimmed = input.trim();
81 if trimmed.is_empty() {
82 return Err(color_eyre::eyre::eyre!(
83 "Note path cannot be empty or whitespace-only"
84 ));
85 }
86 if trimmed.len() == 1 && trimmed.starts_with(PATH_SEPARATOR) {
87 return Err(color_eyre::eyre::eyre!(
88 "Note path cannot be the root separator alone"
89 ));
90 }
91 let raw = if trimmed.starts_with(PATH_SEPARATOR) {
92 trimmed.to_string()
93 } else {
94 let base = if quick_note_path.trim().is_empty() {
95 VaultPath::root().to_string()
96 } else {
97 quick_note_path.trim_end_matches(PATH_SEPARATOR).to_string()
98 };
99 format!("{}{}{}", base, PATH_SEPARATOR, trimmed)
100 };
101 Ok(VaultPath::note_path_from(&raw))
102}
103
104pub fn resolve_content(content: Option<String>) -> color_eyre::eyre::Result<String> {
108 use std::io::IsTerminal;
109 match content {
110 Some(c) => Ok(c),
111 None => {
112 if std::io::stdin().is_terminal() {
113 Ok(String::new())
114 } else {
115 use std::io::Read;
116 let mut buf = String::new();
117 std::io::stdin()
118 .read_to_string(&mut buf)
119 .map_err(|e| color_eyre::eyre::eyre!("Failed to read stdin: {}", e))?;
120 Ok(buf.trim_end_matches(['\n', '\r']).to_string())
121 }
122 }
123 }
124}
125
126pub async fn create_and_init_vault(config_path: Option<PathBuf>) -> Result<(NoteVault, String)> {
131 let (settings, workspace_path, workspace_name) = load_and_resolve_workspace(config_path)?;
132
133 let cache_path = settings.index_for(&workspace_name);
134 let mut vault = NoteVault::new(
137 VaultConfig::new(workspace_path)
138 .with_index(cache_path)
139 .with_backup(true),
140 )
141 .await?;
142 let inbox = resolve_inbox_path(&settings);
143 vault.set_inbox_path(kimun_core::nfs::VaultPath::new(&inbox));
144 vault.validate_and_init().await?;
145
146 Ok((vault, workspace_name))
147}