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