use serde::Serialize; use std::fs; use std::path::Path; #[derive(Serialize, Clone)] struct FileEntry { name: String, path: String, is_dir: bool, #[serde(skip_serializing_if = "Option::is_none")] children: Option>, } #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}! You've been greeted from Rust!", name) } #[tauri::command] fn read_directory(dir_path: String) -> Result, String> { let path = Path::new(&dir_path); if !path.is_dir() { return Err(format!("路径不是目录: {}", dir_path)); } read_dir_recursive(path).map_err(|e| e.to_string()) } fn read_dir_recursive(dir: &Path) -> Result, std::io::Error> { let mut entries = Vec::new(); let read_dir = match fs::read_dir(dir) { Ok(rd) => rd, Err(_) => return Ok(entries), }; for entry in read_dir.flatten() { let path = entry.path(); let name = entry.file_name().to_string_lossy().to_string(); if name.starts_with('.') || name == "node_modules" || name == "target" { continue; } let is_dir = path.is_dir(); let children = if is_dir { Some(read_dir_recursive(&path)?) } else { None }; if is_dir { let child_count = children.as_ref().map_or(0, |c| c.len()); if child_count == 0 && !has_md_files(&path)? { continue; } } else if !name.ends_with(".md") { continue; } entries.push(FileEntry { name, path: path.to_string_lossy().to_string(), is_dir, children, }); } entries.sort_by(|a, b| { if a.is_dir != b.is_dir { b.is_dir.cmp(&a.is_dir) } else { a.name.to_lowercase().cmp(&b.name.to_lowercase()) } }); Ok(entries) } fn has_md_files(dir: &Path) -> Result { for entry in fs::read_dir(dir)? { let entry = entry?; let name = entry.file_name().to_string_lossy().to_string(); if name.starts_with('.') { continue; } if entry.path().is_dir() { if has_md_files(&entry.path())? { return Ok(true); } } else if name.ends_with(".md") { return Ok(true); } } Ok(false) } #[tauri::command] fn read_file_content(file_path: String) -> Result { fs::read_to_string(&file_path).map_err(|e| format!("读取文件失败: {}", e)) } #[tauri::command] fn write_file_content(file_path: String, content: String) -> Result<(), String> { fs::write(&file_path, &content).map_err(|e| format!("保存文件失败: {}", e)) } #[tauri::command] fn create_file(file_path: String) -> Result<(), String> { if Path::new(&file_path).exists() { return Err("文件已存在".to_string()); } if let Some(parent) = Path::new(&file_path).parent() { fs::create_dir_all(parent).map_err(|e| format!("创建目录失败: {}", e))?; } fs::write(&file_path, "").map_err(|e| format!("创建文件失败: {}", e)) } #[tauri::command] fn rename_file(old_path: String, new_path: String) -> Result<(), String> { if Path::new(&new_path).exists() { return Err("目标路径已存在".to_string()); } fs::rename(&old_path, &new_path).map_err(|e| format!("重命名失败: {}", e)) } #[tauri::command] fn delete_file(file_path: String) -> Result<(), String> { let path = Path::new(&file_path); if path.is_dir() { fs::remove_dir_all(&file_path).map_err(|e| format!("删除目录失败: {}", e)) } else { fs::remove_file(&file_path).map_err(|e| format!("删除文件失败: {}", e)) } } #[tauri::command] fn file_exists(file_path: String) -> bool { Path::new(&file_path).exists() } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_dialog::init()) .invoke_handler(tauri::generate_handler![ greet, read_directory, read_file_content, write_file_content, create_file, rename_file, delete_file, file_exists ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }