learning-rust/tauri-app/src-tauri/src/lib.rs

159 lines
4.4 KiB
Rust

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<Vec<FileEntry>>,
}
#[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<Vec<FileEntry>, 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<Vec<FileEntry>, 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<bool, std::io::Error> {
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<String, String> {
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");
}