kimun_notes/cli/
helpers.rs1use crate::settings::AppSettings;
6use color_eyre::eyre::Result;
7use kimun_core::NoteVault;
8use kimun_core::nfs::{PATH_SEPARATOR, VaultPath};
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 if let Some(dir) = &settings.workspace_dir {
25 return Ok((dir.clone(), "default".to_string()));
26 }
27
28 if let Some(ref ws_config) = settings.workspace_config
30 && let Some(entry) = ws_config.get_current_workspace()
31 {
32 let name = ws_config.global.current_workspace.clone();
33 return Ok((entry.path.clone(), name));
34 }
35
36 Err(color_eyre::eyre::eyre!(
37 "No workspace configured. Run 'kimun' to set up a workspace."
38 ))
39}
40
41pub fn load_and_resolve_workspace(
46 config_path: Option<PathBuf>,
47) -> Result<(AppSettings, PathBuf, String)> {
48 let settings = load_settings(config_path)?;
49 let (workspace_path, workspace_name) = resolve_workspace_config(&settings)?;
50 Ok((settings, workspace_path, workspace_name))
51}
52
53pub fn resolve_quick_note_path(settings: &AppSettings) -> String {
56 let root = kimun_core::nfs::VaultPath::root().to_string();
57 if settings.workspace_dir.is_some() {
59 return root;
60 }
61 if let Some(ref ws_config) = settings.workspace_config
63 && let Some(entry) = ws_config.get_current_workspace()
64 {
65 return entry.effective_quick_note_path();
66 }
67 root
68}
69
70pub fn resolve_note_path(input: &str, quick_note_path: &str) -> Result<VaultPath> {
78 let trimmed = input.trim();
79 if trimmed.is_empty() {
80 return Err(color_eyre::eyre::eyre!(
81 "Note path cannot be empty or whitespace-only"
82 ));
83 }
84 if trimmed.len() == 1 && trimmed.starts_with(PATH_SEPARATOR) {
85 return Err(color_eyre::eyre::eyre!(
86 "Note path cannot be the root separator alone"
87 ));
88 }
89 let raw = if trimmed.starts_with(PATH_SEPARATOR) {
90 trimmed.to_string()
91 } else {
92 let base = if quick_note_path.trim().is_empty() {
93 VaultPath::root().to_string()
94 } else {
95 quick_note_path.trim_end_matches(PATH_SEPARATOR).to_string()
96 };
97 format!("{}{}{}", base, PATH_SEPARATOR, trimmed)
98 };
99 Ok(VaultPath::note_path_from(&raw))
100}
101
102pub fn resolve_content(content: Option<String>) -> color_eyre::eyre::Result<String> {
106 use std::io::IsTerminal;
107 match content {
108 Some(c) => Ok(c),
109 None => {
110 if std::io::stdin().is_terminal() {
111 Ok(String::new())
112 } else {
113 use std::io::Read;
114 let mut buf = String::new();
115 std::io::stdin()
116 .read_to_string(&mut buf)
117 .map_err(|e| color_eyre::eyre::eyre!("Failed to read stdin: {}", e))?;
118 Ok(buf.trim_end_matches(['\n', '\r']).to_string())
119 }
120 }
121 }
122}
123
124pub async fn create_and_init_vault(config_path: Option<PathBuf>) -> Result<(NoteVault, String)> {
129 let (_settings, workspace_path, workspace_name) = load_and_resolve_workspace(config_path)?;
130
131 let vault = NoteVault::new(&workspace_path).await?;
132 vault.validate_and_init().await?;
133
134 Ok((vault, workspace_name))
135}