From d0fb87cf3a8b14e00fd4145a36b5f71f8aac0ff3 Mon Sep 17 00:00:00 2001 From: Yuri Karamian Date: Mon, 17 Aug 2026 19:27:05 +0200 Subject: [PATCH] style: format Rust code --- src-tauri/build.rs | 2 +- src-tauri/src/ai.rs | 43 ++-- src-tauri/src/commands.rs | 312 ++++++++++++++++++++---------- src-tauri/src/main.rs | 2 +- src-tauri/src/search/mod.rs | 16 +- src-tauri/src/sync.rs | 23 ++- src-tauri/src/types.rs | 5 +- src-tauri/src/vault/import.rs | 62 +++--- src-tauri/src/vault/operations.rs | 93 ++++++--- 9 files changed, 368 insertions(+), 190 deletions(-) diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 795b9b7..d860e1e 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,3 +1,3 @@ fn main() { - tauri_build::build() + tauri_build::build() } diff --git a/src-tauri/src/ai.rs b/src-tauri/src/ai.rs index e60d7dc..d68472f 100644 --- a/src-tauri/src/ai.rs +++ b/src-tauri/src/ai.rs @@ -23,7 +23,11 @@ pub fn ai_request( let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { // Handle all API keys as optional; ollama and v1 completions doesnt always require it. - let key_opt = if api_key.is_empty() { None } else { Some(api_key.as_str()) }; + let key_opt = if api_key.is_empty() { + None + } else { + Some(api_key.as_str()) + }; let result = match provider.as_str() { "openai" => { stream_openai( @@ -99,7 +103,10 @@ pub fn ai_request( /// so both `https://host` and `https://host/v1` work (we append `/v1/chat/completions`). fn normalize_openai_base(base: &str) -> String { let b = base.trim().trim_end_matches('/'); - b.strip_suffix("/v1").unwrap_or(b).trim_end_matches('/').to_string() + b.strip_suffix("/v1") + .unwrap_or(b) + .trim_end_matches('/') + .to_string() } async fn stream_anthropic( @@ -269,9 +276,7 @@ async fn stream_openai( body["temperature"] = json!(0.7); } - let mut req = client - .post(url) - .header("content-type", "application/json"); + let mut req = client.post(url).header("content-type", "application/json"); if let Some(key) = api_key { req = req.header("Authorization", format!("Bearer {}", key)); @@ -381,7 +386,11 @@ pub async fn test_connection( model: &str, base_url: Option<&str>, ) -> Result { - let key_opt = if api_key.is_empty() { None } else { Some(api_key) }; + let key_opt = if api_key.is_empty() { + None + } else { + Some(api_key) + }; match provider { "openai" => test_openai(OPENAI_API_URL, Some(api_key), model).await, "ollama" => { @@ -435,14 +444,18 @@ async fn test_anthropic(api_key: &str, model: &str) -> Result { } async fn test_openai(url: &str, api_key: Option<&str>, model: &str) -> Result { - let client = Client::new(); - let is_gpt5 = model.starts_with("gpt-5"); - let token_key = if is_gpt5 { "max_completion_tokens" } else { "max_tokens" }; + let client = Client::new(); + let is_gpt5 = model.starts_with("gpt-5"); + let token_key = if is_gpt5 { + "max_completion_tokens" + } else { + "max_tokens" + }; - let body = json!({ - "model": model, - token_key: 20, - "messages": [ + let body = json!({ + "model": model, + token_key: 20, + "messages": [ { "role": "user", "content": "Hi" @@ -450,9 +463,7 @@ async fn test_openai(url: &str, api_key: Option<&str>, model: &str) -> Result, path: &str) { - let search = state.search_index.lock().ok().and_then(|g| g.clone()); - if let Some(search) = search { - let p = path.to_string(); - std::thread::spawn(move || { let _ = search.index_note(&p); }); - } + let search = state.search_index.lock().ok().and_then(|g| g.clone()); + if let Some(search) = search { + let p = path.to_string(); + std::thread::spawn(move || { + let _ = search.index_note(&p); + }); + } } fn remove_note_bg(state: &State<'_, AppState>, path: &str) { - let search = state.search_index.lock().ok().and_then(|g| g.clone()); - if let Some(search) = search { - let p = path.to_string(); - std::thread::spawn(move || { let _ = search.remove_note(&p); }); - } + let search = state.search_index.lock().ok().and_then(|g| g.clone()); + if let Some(search) = search { + let p = path.to_string(); + std::thread::spawn(move || { + let _ = search.remove_note(&p); + }); + } } fn clear_vault_runtime(state: &State<'_, AppState>) -> Result<(), String> { @@ -189,9 +193,7 @@ pub async fn choose_external_vault( if bookmark_was_registered { let _ = app.ios_vault_access().rollback_staged(); } else { - let _ = app - .ios_vault_access() - .forget_bookmark(&result.bookmark_id); + let _ = app.ios_vault_access().forget_bookmark(&result.bookmark_id); } return Err(error); } @@ -353,7 +355,10 @@ pub fn set_accent_color(state: State<'_, AppState>, color: String) -> Result<(), } #[tauri::command] -pub fn save_custom_theme(state: State<'_, AppState>, theme: crate::types::CustomTheme) -> Result<(), String> { +pub fn save_custom_theme( + state: State<'_, AppState>, + theme: crate::types::CustomTheme, +) -> Result<(), String> { let mut config = state.config.lock().map_err(|e| e.to_string())?; if let Some(pos) = config.custom_themes.iter().position(|t| t.id == theme.id) { config.custom_themes[pos] = theme; @@ -407,9 +412,16 @@ mod custom_theme_reference_tests { } #[tauri::command] -pub fn export_custom_theme(state: State<'_, AppState>, id: String, path: String) -> Result<(), String> { +pub fn export_custom_theme( + state: State<'_, AppState>, + id: String, + path: String, +) -> Result<(), String> { let config = state.config.lock().map_err(|e| e.to_string())?; - let theme = config.custom_themes.iter().find(|t| t.id == id) + let theme = config + .custom_themes + .iter() + .find(|t| t.id == id) .ok_or_else(|| "Theme not found".to_string())?; let export = serde_json::json!({ "version": 1, "themes": [theme] }); let data = serde_json::to_string_pretty(&export).map_err(|e| e.to_string())?; @@ -418,11 +430,15 @@ pub fn export_custom_theme(state: State<'_, AppState>, id: String, path: String) } #[tauri::command] -pub fn import_custom_themes(state: State<'_, AppState>, path: String) -> Result, String> { +pub fn import_custom_themes( + state: State<'_, AppState>, + path: String, +) -> Result, String> { let data = std::fs::read_to_string(&path).map_err(|e| e.to_string())?; let parsed: serde_json::Value = serde_json::from_str(&data).map_err(|e| e.to_string())?; - let themes: Vec = serde_json::from_value(parsed["themes"].clone()) - .map_err(|e| format!("Invalid theme file: {}", e))?; + let themes: Vec = + serde_json::from_value(parsed["themes"].clone()) + .map_err(|e| format!("Invalid theme file: {}", e))?; let mut config = state.config.lock().map_err(|e| e.to_string())?; for theme in &themes { if let Some(pos) = config.custom_themes.iter().position(|t| t.id == theme.id) { @@ -464,11 +480,7 @@ pub fn set_line_height(state: State<'_, AppState>, height: f64) -> Result<(), St } #[tauri::command] -pub fn set_ui_scale( - app: AppHandle, - state: State<'_, AppState>, - scale: f64, -) -> Result<(), String> { +pub fn set_ui_scale(app: AppHandle, state: State<'_, AppState>, scale: f64) -> Result<(), String> { let mut config = state.config.lock().map_err(|e| e.to_string())?; config.ui_scale = Some(scale); save_app_config(&config)?; @@ -602,7 +614,11 @@ pub fn move_notebook( pub fn delete_notebook(state: State<'_, AppState>, path: String) -> Result<(), String> { let vault_path = { let config = state.config.lock().map_err(|e| e.to_string())?; - config.active_vault.as_ref().ok_or("No active vault")?.clone() + config + .active_vault + .as_ref() + .ok_or("No active vault")? + .clone() }; operations::delete_notebook(&vault_path, &path) } @@ -651,8 +667,8 @@ pub fn save_note( operations::save_note(&vault_path, &path, &meta, &body)?; - // Re-index note so search picks up changes (background to avoid blocking on FUSE fsync) - index_note_bg(&state, &path); + // Re-index note so search picks up changes (background to avoid blocking on FUSE fsync) + index_note_bg(&state, &path); Ok(()) } @@ -663,7 +679,11 @@ pub fn duplicate_note( path: String, ) -> Result { let config = state.config.lock().map_err(|e| e.to_string())?; - let vault = config.active_vault.as_ref().ok_or("No active vault")?.clone(); + let vault = config + .active_vault + .as_ref() + .ok_or("No active vault")? + .clone(); drop(config); let entry = operations::duplicate_note(&path, &vault)?; @@ -677,31 +697,43 @@ pub fn create_note( notebook_relative: Option, title: String, ) -> Result { - let config = state.config.lock().map_err(|e| e.to_string())?; - let vault_path = config.active_vault.as_ref().ok_or("No active vault")?; - let entry = operations::create_note(vault_path, notebook_relative.as_deref(), &title)?; - - // Index new note (background to avoid blocking on FUSE fsync) - index_note_bg(&state, &entry.path); - - Ok(entry) -} - -#[tauri::command] -pub fn create_daily_note(state: State<'_, AppState>, date: Option) -> Result { let config = state.config.lock().map_err(|e| e.to_string())?; let vault_path = config.active_vault.as_ref().ok_or("No active vault")?; - let entry = operations::create_daily_note(vault_path, date.as_deref(), &config.daily_title_format)?; + let entry = operations::create_note(vault_path, notebook_relative.as_deref(), &title)?; - index_note_bg(&state, &entry.path); + // Index new note (background to avoid blocking on FUSE fsync) + index_note_bg(&state, &entry.path); Ok(entry) } #[tauri::command] -pub fn rename_note(state: State<'_, AppState>, path: String, new_title: String) -> Result { +pub fn create_daily_note( + state: State<'_, AppState>, + date: Option, +) -> Result { let config = state.config.lock().map_err(|e| e.to_string())?; - let vault_path = config.active_vault.as_ref().ok_or("No active vault")?.clone(); + let vault_path = config.active_vault.as_ref().ok_or("No active vault")?; + let entry = + operations::create_daily_note(vault_path, date.as_deref(), &config.daily_title_format)?; + + index_note_bg(&state, &entry.path); + + Ok(entry) +} + +#[tauri::command] +pub fn rename_note( + state: State<'_, AppState>, + path: String, + new_title: String, +) -> Result { + let config = state.config.lock().map_err(|e| e.to_string())?; + let vault_path = config + .active_vault + .as_ref() + .ok_or("No active vault")? + .clone(); drop(config); operations::rename_note(&path, &new_title, &vault_path) } @@ -712,8 +744,8 @@ pub fn delete_note(state: State<'_, AppState>, path: String) -> Result<(), Strin let vault_path = config.active_vault.as_ref().ok_or("No active vault")?; operations::delete_note(vault_path, &path)?; - // Remove from index (background to avoid blocking on FUSE fsync) - remove_note_bg(&state, &path); + // Remove from index (background to avoid blocking on FUSE fsync) + remove_note_bg(&state, &path); Ok(()) } @@ -809,10 +841,7 @@ pub fn get_all_note_titles(state: State<'_, AppState>) -> Result) -> Result) -> Result) -> Result = Vec::new(); let mut edge_map: HashMap<(usize, usize), usize> = HashMap::new(); - let add_edge = |edges: &mut Vec, edge_map: &mut HashMap<(usize, usize), usize>, src: usize, tgt: usize| { - if src == tgt { return; } - if edge_map.contains_key(&(src, tgt)) { return; } // exact duplicate + let add_edge = |edges: &mut Vec, + edge_map: &mut HashMap<(usize, usize), usize>, + src: usize, + tgt: usize| { + if src == tgt { + return; + } + if edge_map.contains_key(&(src, tgt)) { + return; + } // exact duplicate if let Some(&rev_idx) = edge_map.get(&(tgt, src)) { // Reverse direction already exists - mark it as bidirectional edges[rev_idx].bidirectional = true; } else { let idx = edges.len(); edge_map.insert((src, tgt), idx); - edges.push(crate::types::GraphEdge { source: src, target: tgt, bidirectional: false }); + edges.push(crate::types::GraphEdge { + source: src, + target: tgt, + bidirectional: false, + }); } }; @@ -970,14 +1024,19 @@ pub fn get_graph_data(state: State<'_, AppState>) -> Result Option { let trimmed = raw.trim_start(); - if !trimmed.starts_with("---") { return None; } + if !trimmed.starts_with("---") { + return None; + } // Find the closing --- let after_open = &trimmed[3..]; let end = after_open.find("\n---")?; @@ -987,8 +1046,10 @@ fn extract_title_fast(raw: &str) -> Option { if let Some(title) = line.strip_prefix("title:") { let val = title.trim(); // Strip surrounding quotes - if (val.starts_with('"') && val.ends_with('"')) || (val.starts_with('\'') && val.ends_with('\'')) { - return Some(val[1..val.len()-1].to_string()); + if (val.starts_with('"') && val.ends_with('"')) + || (val.starts_with('\'') && val.ends_with('\'')) + { + return Some(val[1..val.len() - 1].to_string()); } if !val.is_empty() { return Some(val.to_string()); @@ -1031,7 +1092,11 @@ pub fn get_tasks(state: State<'_, AppState>) -> Result r, Err(_) => return out, }; - let filename = path.file_name().unwrap_or_default().to_string_lossy().to_string(); + let filename = path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); let (meta, body) = crate::vault::frontmatter::parse_note(&raw, &filename); let note_path = path.to_string_lossy().to_string(); for (i, line) in body.lines().enumerate() { @@ -1044,7 +1109,11 @@ pub fn get_tasks(state: State<'_, AppState>) -> Result Result { let vault_path = { let config = state.config.lock().map_err(|e| e.to_string())?; - config.active_vault.as_ref().ok_or("No active vault")?.clone() + config + .active_vault + .as_ref() + .ok_or("No active vault")? + .clone() }; operations::restore_note(&vault_path, &trash_path, dest_notebook.as_deref()) } @@ -1290,7 +1363,11 @@ pub fn restore_note( pub fn restore_notebook(state: State<'_, AppState>, trash_path: String) -> Result { let vault_path = { let config = state.config.lock().map_err(|e| e.to_string())?; - config.active_vault.as_ref().ok_or("No active vault")?.clone() + config + .active_vault + .as_ref() + .ok_or("No active vault")? + .clone() }; operations::restore_notebook(&vault_path, &trash_path) } @@ -1299,7 +1376,11 @@ pub fn restore_notebook(state: State<'_, AppState>, trash_path: String) -> Resul pub fn permanent_delete(state: State<'_, AppState>, path: String) -> Result<(), String> { let vault_path = { let config = state.config.lock().map_err(|e| e.to_string())?; - config.active_vault.as_ref().ok_or("No active vault")?.clone() + config + .active_vault + .as_ref() + .ok_or("No active vault")? + .clone() }; operations::permanent_delete(&vault_path, &path) } @@ -1359,8 +1440,11 @@ pub fn read_clipboard_image() -> Result, String> { // Encode RGBA data to PNG let mut buf: Vec = Vec::new(); { - let mut encoder = - png::Encoder::new(std::io::Cursor::new(&mut buf), img.width as u32, img.height as u32); + let mut encoder = png::Encoder::new( + std::io::Cursor::new(&mut buf), + img.width as u32, + img.height as u32, + ); encoder.set_color(png::ColorType::Rgba); encoder.set_depth(png::BitDepth::Eight); let mut writer = encoder @@ -1384,8 +1468,8 @@ pub fn read_clipboard_image() -> Result, String> { #[tauri::command] pub fn copy_image_to_clipboard(path: String) -> Result<(), String> { let data = std::fs::read(&path).map_err(|e| format!("Failed to read image: {}", e))?; - let img = image::load_from_memory(&data) - .map_err(|e| format!("Failed to decode image: {}", e))?; + let img = + image::load_from_memory(&data).map_err(|e| format!("Failed to decode image: {}", e))?; let rgba = img.to_rgba8(); let (w, h) = rgba.dimensions(); let img_data = arboard::ImageData { @@ -1393,9 +1477,10 @@ pub fn copy_image_to_clipboard(path: String) -> Result<(), String> { height: h as usize, bytes: std::borrow::Cow::Owned(rgba.into_raw()), }; - let mut clipboard = arboard::Clipboard::new() - .map_err(|e| format!("Clipboard init failed: {}", e))?; - clipboard.set_image(img_data) + let mut clipboard = + arboard::Clipboard::new().map_err(|e| format!("Clipboard init failed: {}", e))?; + clipboard + .set_image(img_data) .map_err(|e| format!("Failed to set clipboard image: {}", e))?; Ok(()) } @@ -1410,8 +1495,8 @@ pub fn copy_image_to_clipboard(_path: String) -> Result<(), String> { #[cfg(desktop)] #[tauri::command] pub fn copy_png_to_clipboard(data: Vec) -> Result<(), String> { - let img = image::load_from_memory(&data) - .map_err(|e| format!("Failed to decode image: {}", e))?; + let img = + image::load_from_memory(&data).map_err(|e| format!("Failed to decode image: {}", e))?; let rgba = img.to_rgba8(); let (w, h) = rgba.dimensions(); let img_data = arboard::ImageData { @@ -1419,9 +1504,10 @@ pub fn copy_png_to_clipboard(data: Vec) -> Result<(), String> { height: h as usize, bytes: std::borrow::Cow::Owned(rgba.into_raw()), }; - let mut clipboard = arboard::Clipboard::new() - .map_err(|e| format!("Clipboard init failed: {}", e))?; - clipboard.set_image(img_data) + let mut clipboard = + arboard::Clipboard::new().map_err(|e| format!("Clipboard init failed: {}", e))?; + clipboard + .set_image(img_data) .map_err(|e| format!("Failed to set clipboard image: {}", e))?; Ok(()) } @@ -1706,7 +1792,11 @@ fn scan_orphaned_attachments(vault: &str) -> Result, String> let entry = entry.map_err(|e| e.to_string())?; let p = entry.path(); if p.is_file() { - let name = p.file_name().unwrap_or_default().to_string_lossy().to_string(); + let name = p + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); let size = entry.metadata().map(|m| m.len()).unwrap_or(0); files.push((name, size)); } @@ -1715,7 +1805,10 @@ fn scan_orphaned_attachments(vault: &str) -> Result, String> return Ok(Vec::new()); } let mut haystack = String::new(); - for entry in walkdir::WalkDir::new(vault).into_iter().filter_map(|e| e.ok()) { + for entry in walkdir::WalkDir::new(vault) + .into_iter() + .filter_map(|e| e.ok()) + { let p = entry.path(); if p.is_file() && p.extension().and_then(|x| x.to_str()) == Some("md") { if let Ok(content) = std::fs::read_to_string(p) { @@ -1887,7 +1980,6 @@ pub fn write_bytes_to(destination: String, data: Vec) -> Result<(), String> Ok(()) } - // ── Backup ── #[tauri::command] @@ -2068,7 +2160,8 @@ pub fn set_ai_settings( config.ollama_api_key = ollama_api_key.filter(|k| !k.is_empty()); } Some("openai_compatible") => { - config.openai_compatible_base_url = openai_compatible_base_url.filter(|u| !u.trim().is_empty()); + config.openai_compatible_base_url = + openai_compatible_base_url.filter(|u| !u.trim().is_empty()); config.openai_compatible_api_key = openai_compatible_api_key.filter(|k| !k.is_empty()); } _ => config.ai_api_key = key, @@ -2091,7 +2184,9 @@ pub fn test_ai_connection(app: AppHandle) -> Result<(), String> { .unwrap_or_else(|| "anthropic".to_string()); let key = match provider.as_str() { "ollama" => Some(config.ollama_api_key.clone().unwrap_or_default()), - "openai_compatible" => Some(config.openai_compatible_api_key.clone().unwrap_or_default()), + "openai_compatible" => { + Some(config.openai_compatible_api_key.clone().unwrap_or_default()) + } "openai" => config.openai_api_key.clone(), _ => config.ai_api_key.clone(), } @@ -2133,11 +2228,7 @@ pub fn test_ai_connection(app: AppHandle) -> Result<(), String> { // ── Sync (WebDAV) ── -fn vault_matches_identity( - vault: &VaultConfig, - path: &str, - bookmark_id: Option<&str>, -) -> bool { +fn vault_matches_identity(vault: &VaultConfig, path: &str, bookmark_id: Option<&str>) -> bool { if let Some(bookmark_id) = bookmark_id { vault.bookmark_id.as_deref() == Some(bookmark_id) } else { @@ -2282,9 +2373,7 @@ pub fn sync_now(app: AppHandle) -> Result<(), String> { .clone() .ok_or_else(|| "No active vault".to_string()) .and_then(|vault| { - sync_config_from(&config).map(|cfg| { - (vault, config.active_bookmark_id.clone(), cfg) - }) + sync_config_from(&config).map(|cfg| (vault, config.active_bookmark_id.clone(), cfg)) }); match gathered { Ok(vault_config) => vault_config, @@ -2298,7 +2387,9 @@ pub fn sync_now(app: AppHandle) -> Result<(), String> { std::thread::spawn(move || { use tauri::Emitter; let result = crate::sync::run_sync(app.clone(), vault.clone(), cfg); - app.state::().syncing.store(false, Ordering::SeqCst); + app.state::() + .syncing + .store(false, Ordering::SeqCst); match result { Ok(summary) => { let ts = chrono::Utc::now().to_rfc3339(); @@ -2316,7 +2407,10 @@ pub fn sync_now(app: AppHandle) -> Result<(), String> { ); } Err(e) => { - let _ = app.emit("sync-error", serde_json::json!({ "success": false, "error": e })); + let _ = app.emit( + "sync-error", + serde_json::json!({ "success": false, "error": e }), + ); } } }); @@ -2340,7 +2434,9 @@ pub fn ai_ask( .unwrap_or_else(|| "anthropic".to_string()); let key = match provider.as_str() { "ollama" => Some(config.ollama_api_key.clone().unwrap_or_default()), - "openai_compatible" => Some(config.openai_compatible_api_key.clone().unwrap_or_default()), + "openai_compatible" => { + Some(config.openai_compatible_api_key.clone().unwrap_or_default()) + } "openai" => config.openai_api_key.clone(), _ => config.ai_api_key.clone(), } @@ -2445,7 +2541,9 @@ fn migrate_global_sync_to_vault(config: &mut AppConfig) -> bool { if config.sync_provider.is_none() && config.webdav_url.is_none() { return false; } - let Ok(active_index) = active_vault_index(config) else { return false; }; + let Ok(active_index) = active_vault_index(config) else { + return false; + }; let g_provider = config.sync_provider.clone(); let g_url = config.webdav_url.clone(); let g_user = config.webdav_username.clone(); @@ -2555,13 +2653,15 @@ pub fn get_install_type() -> String { } else if std::path::Path::new("/var/lib/dpkg/info/helix-notes.list").exists() { "deb".to_string() } else if std::path::Path::new("/var/lib/pacman/local").exists() - && ["helixnotes", "helixnotes-bin", "helixnotes-appimage-bin"].iter().any(|pkg| { - std::process::Command::new("pacman") - .args(["-Q", pkg]) - .output() - .map(|o| o.status.success()) - .unwrap_or(false) - }) + && ["helixnotes", "helixnotes-bin", "helixnotes-appimage-bin"] + .iter() + .any(|pkg| { + std::process::Command::new("pacman") + .args(["-Q", pkg]) + .output() + .map(|o| o.status.success()) + .unwrap_or(false) + }) { "aur".to_string() } else if std::env::var("APPIMAGE").is_ok() { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ad5fe83..69c3a72 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,5 +2,5 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] fn main() { - app_lib::run(); + app_lib::run(); } diff --git a/src-tauri/src/search/mod.rs b/src-tauri/src/search/mod.rs index b1e34bd..6d0f6a9 100644 --- a/src-tauri/src/search/mod.rs +++ b/src-tauri/src/search/mod.rs @@ -26,7 +26,10 @@ fn vault_index_base(vault_path: &str) -> Option { use sha2::{Digest, Sha256}; let mut hasher = Sha256::new(); hasher.update(vault_path.as_bytes()); - let key: String = hasher.finalize()[..8].iter().map(|b| format!("{:02x}", b)).collect(); + let key: String = hasher.finalize()[..8] + .iter() + .map(|b| format!("{:02x}", b)) + .collect(); dirs::data_local_dir().map(|d| d.join("helixnotes").join("search").join(key)) } @@ -235,7 +238,9 @@ impl SearchIndex { // before the writer is created, so both indexing and querying use it). index.tokenizers().register( "cjk", - TextAnalyzer::builder(CjkTokenizer).filter(LowerCaser).build(), + TextAnalyzer::builder(CjkTokenizer) + .filter(LowerCaser) + .build(), ); #[cfg(mobile)] @@ -376,9 +381,10 @@ impl SearchIndex { )); vec![(Occur::Should, exact)] } else { - let prefix: Box = Box::new(PhrasePrefixQuery::new( - vec![Term::from_field_text(field, term)], - )); + let prefix: Box = + Box::new(PhrasePrefixQuery::new(vec![Term::from_field_text( + field, term, + )])); let fuzzy: Box = Box::new(FuzzyTermQuery::new( Term::from_field_text(field, term), 1, diff --git a/src-tauri/src/sync.rs b/src-tauri/src/sync.rs index d61e0f5..f830ed4 100644 --- a/src-tauri/src/sync.rs +++ b/src-tauri/src/sync.rs @@ -81,7 +81,10 @@ fn sha256_hex(bytes: &[u8]) -> String { } fn normalize_etag(s: &str) -> String { - s.trim().trim_start_matches("W/").trim_matches('"').to_string() + s.trim() + .trim_start_matches("W/") + .trim_matches('"') + .to_string() } /// Percent-encode each path segment, keeping the `/` separators. @@ -265,7 +268,11 @@ impl WebdavClient { .send() .map_err(|e| e.to_string())?; if !resp.status().is_success() { - return Err(format!("GET {} failed: HTTP {}", relpath, resp.status().as_u16())); + return Err(format!( + "GET {} failed: HTTP {}", + relpath, + resp.status().as_u16() + )); } Ok(resp.bytes().map_err(|e| e.to_string())?.to_vec()) } @@ -279,7 +286,11 @@ impl WebdavClient { .send() .map_err(|e| e.to_string())?; if !resp.status().is_success() { - return Err(format!("PUT {} failed: HTTP {}", relpath, resp.status().as_u16())); + return Err(format!( + "PUT {} failed: HTTP {}", + relpath, + resp.status().as_u16() + )); } Ok(resp .headers() @@ -671,7 +682,11 @@ pub fn test_connection(cfg: WebdavConfig) -> Result { /// Run a full sync. Mutes the file watcher while applying local writes, then /// rebuilds the search index. Returns a summary of what changed. -pub fn run_sync(app: tauri::AppHandle, vault: String, cfg: WebdavConfig) -> Result { +pub fn run_sync( + app: tauri::AppHandle, + vault: String, + cfg: WebdavConfig, +) -> Result { use std::sync::atomic::Ordering; let state = app.state::(); diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index b8b278c..e5080b9 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -549,10 +549,7 @@ mod startup_view_tests { assert!(!config.show_note_switcher); let mut value = serde_json::to_value(config).unwrap(); - value - .as_object_mut() - .unwrap() - .remove("show_note_switcher"); + value.as_object_mut().unwrap().remove("show_note_switcher"); let config: AppConfig = serde_json::from_value(value).unwrap(); assert!(!config.show_note_switcher); diff --git a/src-tauri/src/vault/import.rs b/src-tauri/src/vault/import.rs index 711a4f4..9f840ec 100644 --- a/src-tauri/src/vault/import.rs +++ b/src-tauri/src/vault/import.rs @@ -112,7 +112,11 @@ pub fn import(vault_path: &str) -> Result { "png" | "jpg" | "jpeg" | "gif" | "svg" | "webp" | "bmp" | "ico" | "pdf" ); if is_embeddable { - let alt = if is_dimension_spec(alt_param) { "" } else { alt_param }; + let alt = if is_dimension_spec(alt_param) { + "" + } else { + alt_param + }; result.links_converted += 1; format!("![{}]({})", alt, link_target) } else { @@ -135,7 +139,11 @@ pub fn import(vault_path: &str) -> Result { let (file_part, anchor) = split_anchor(note_ref); if file_part.is_empty() { if let Some(a) = anchor { - let display = if display_param.is_empty() { a } else { display_param }; + let display = if display_param.is_empty() { + a + } else { + display_param + }; result.links_converted += 1; return format!("[{}](#{})", display, a); } @@ -163,8 +171,22 @@ pub fn import(vault_path: &str) -> Result { .to_string(); content = after_links; - content = fix_md_image_refs(&content, &md_img_re, vault, note_dir, &file_index, &mut result.links_converted); - content = fix_md_link_refs(&content, &md_link_re, vault, note_dir, &file_index, &mut result.links_converted); + content = fix_md_image_refs( + &content, + &md_img_re, + vault, + note_dir, + &file_index, + &mut result.links_converted, + ); + content = fix_md_link_refs( + &content, + &md_link_re, + vault, + note_dir, + &file_index, + &mut result.links_converted, + ); if result.links_converted > links_before { changed = true; @@ -387,9 +409,7 @@ fn file_created(path: &Path) -> Option> { .ok() .and_then(|m| m.created().ok()) .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok()) - .and_then(|d| { - chrono::DateTime::from_timestamp(d.as_secs() as i64, d.subsec_nanos()) - }) + .and_then(|d| chrono::DateTime::from_timestamp(d.as_secs() as i64, d.subsec_nanos())) } fn file_modified(path: &Path) -> Option> { @@ -397,9 +417,7 @@ fn file_modified(path: &Path) -> Option> { .ok() .and_then(|m| m.modified().ok()) .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok()) - .and_then(|d| { - chrono::DateTime::from_timestamp(d.as_secs() as i64, d.subsec_nanos()) - }) + .and_then(|d| chrono::DateTime::from_timestamp(d.as_secs() as i64, d.subsec_nanos())) } fn convert_syntax(content: &str, highlight_re: &Regex, comment_re: &Regex) -> String { @@ -511,7 +529,9 @@ fn fix_md_link_refs( let display = &caps[1]; let href = &caps[2]; let decoded = percent_decode(href); - if decoded.starts_with("http") || decoded.starts_with('/') || decoded.starts_with("data:") + if decoded.starts_with("http") + || decoded.starts_with('/') + || decoded.starts_with("data:") || decoded.starts_with('#') { return format!("[{}]({})", display, href); @@ -547,9 +567,7 @@ fn fix_md_link_refs( .to_string() } -fn move_attachments( - vault: &Path, -) -> Result, String> { +fn move_attachments(vault: &Path) -> Result, String> { let attachments_dir = vault.join(".helixnotes").join("attachments"); let _ = std::fs::create_dir_all(&attachments_dir); @@ -599,10 +617,7 @@ fn move_attachments( Ok(moved) } -fn rewrite_attachment_refs( - vault: &Path, - moved: &HashMap, -) -> Result<(), String> { +fn rewrite_attachment_refs(vault: &Path, moved: &HashMap) -> Result<(), String> { let md_ref = Regex::new(r"(!?\[[^\]]*\])\(([^)]+)\)").map_err(|e| e.to_string())?; let md_files: Vec<_> = walkdir::WalkDir::new(vault) @@ -717,7 +732,8 @@ fn is_dimension_spec(s: &str) -> bool { if s.is_empty() { return false; } - s.chars().all(|c| c.is_ascii_digit() || c == 'x' || c == 'X') + s.chars() + .all(|c| c.is_ascii_digit() || c == 'x' || c == 'X') } fn resolve_wiki_ref(file_index: &HashMap, reference: &str) -> String { @@ -905,7 +921,10 @@ mod tests { extract_heading_title("# My Title\n\nBody"), Some("My Title".to_string()) ); - assert_eq!(extract_heading_title("\n\n# Spaced Title"), Some("Spaced Title".to_string())); + assert_eq!( + extract_heading_title("\n\n# Spaced Title"), + Some("Spaced Title".to_string()) + ); assert_eq!(extract_heading_title("Body without heading"), None); assert_eq!(extract_heading_title("## Subheading"), None); assert_eq!(extract_heading_title(""), None); @@ -945,8 +964,7 @@ mod tests { #[test] fn test_normalize_tags_strips_hash() { - let mapping: serde_yaml::Mapping = - serde_yaml::from_str("tags:\n - \"#hashed\"").unwrap(); + let mapping: serde_yaml::Mapping = serde_yaml::from_str("tags:\n - \"#hashed\"").unwrap(); let tags = normalize_tags(&mapping); assert_eq!(tags, vec!["hashed"]); } diff --git a/src-tauri/src/vault/operations.rs b/src-tauri/src/vault/operations.rs index 68939d2..998fa3d 100644 --- a/src-tauri/src/vault/operations.rs +++ b/src-tauri/src/vault/operations.rs @@ -51,7 +51,10 @@ fn ensure_vault_content_dir(vault_path: &str, requested_path: &Path) -> Result

Result { let requested = ensure_vault_content_path(vault_path, requested_path, false)?; if !requested.is_file() - || requested.extension().and_then(|extension| extension.to_str()) != Some("md") + || requested + .extension() + .and_then(|extension| extension.to_str()) + != Some("md") { return Err("Note path must point to a Markdown file".to_string()); } @@ -65,7 +68,10 @@ fn ensure_readable_note_path(vault_path: &str, requested_path: &Path) -> Result< let trashed_note = ensure_trash_entry(vault_path, requested_path)?; if !trashed_note.is_file() - || trashed_note.extension().and_then(|extension| extension.to_str()) != Some("md") + || trashed_note + .extension() + .and_then(|extension| extension.to_str()) + != Some("md") { return Err("Note path must point to a Markdown file".to_string()); } @@ -235,7 +241,11 @@ fn scan_dir_recursive(dir: &Path, vault_root: &str) -> Vec { paths .par_iter() .map(|path| { - let name = path.file_name().unwrap_or_default().to_string_lossy().to_string(); + let name = path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); let relative = path .strip_prefix(root) .unwrap_or(path) @@ -286,7 +296,11 @@ fn scan_dir_with_count(dir: &Path, vault_root: &str) -> (Vec, usi let entries: Vec = paths .par_iter() .map(|path| { - let name = path.file_name().unwrap_or_default().to_string_lossy().to_string(); + let name = path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); let relative = path .strip_prefix(root) .unwrap_or(path) @@ -341,7 +355,12 @@ pub fn scan_notes(vault_path: &str, notebook_path: Option<&str>) -> Result) -> Result target_date.format("%d/%m/%Y").to_string(), _ => { let locale = get_system_locale(); - target_date.format_localized("%B %d, %Y", locale).to_string() + target_date + .format_localized("%B %d, %Y", locale) + .to_string() } }; @@ -935,7 +958,13 @@ pub fn rename_note(path: &str, new_title: &str, vault_path: &str) -> Result c, @@ -1014,22 +1049,13 @@ fn update_wikilinks_after_rename( // If another note shares the same title, these would be ambiguous. if old_title != new_title && title_is_unique { // 1. Short title ref: [[Old Title]] → [[New Title]] - result = result.replace( - &format!("[[{}]]", old_title), - &format!("[[{}]]", new_title), - ); + result = result.replace(&format!("[[{}]]", old_title), &format!("[[{}]]", new_title)); // 2. Short title with alias: [[Old Title|display]] → [[New Title|display]] - result = result.replace( - &format!("[[{}|", old_title), - &format!("[[{}|", new_title), - ); + result = result.replace(&format!("[[{}|", old_title), &format!("[[{}|", new_title)); // 3. Short title as alias display: [[ref|Old Title]] → [[ref|New Title]] - result = result.replace( - &format!("|{}]]", old_title), - &format!("|{}]]", new_title), - ); + result = result.replace(&format!("|{}]]", old_title), &format!("|{}]]", new_title)); } // Path-based rules are always safe (paths are unique). @@ -1144,7 +1170,10 @@ fn cleanup_empty_trash_dir(vault_path: &str, dir: Option<&Path>) { pub fn get_trash_contents(vault_path: &str) -> Result { let trash_dir = helixnotes_dir(vault_path).join("trash"); if !trash_dir.exists() { - return Ok(TrashContents { notes: Vec::new(), notebooks: Vec::new() }); + return Ok(TrashContents { + notes: Vec::new(), + notebooks: Vec::new(), + }); } let vault_root = Path::new(vault_path); @@ -1163,7 +1192,10 @@ pub fn get_trash_contents(vault_path: &str) -> Result { .min_depth(1) .into_iter() .filter_map(|e| e.ok()) - .filter(|e| e.path().is_file() && e.path().extension().and_then(|x| x.to_str()) == Some("md")) + .filter(|e| { + e.path().is_file() + && e.path().extension().and_then(|x| x.to_str()) == Some("md") + }) .count(); let dirname = path.file_name().unwrap_or_default().to_string_lossy(); // Strip timestamp prefix to get original notebook name @@ -1239,7 +1271,9 @@ pub fn restore_notebook(vault_path: &str, trash_path: &str) -> Result