use serde::Serialize; use std::fs; use std::path::PathBuf; use std::sync::mpsc; use tauri_plugin_dialog::DialogExt; #[derive(Serialize)] pub struct AppInfo { pub name: String, pub version: String, pub tauri_version: String, } #[tauri::command] pub fn get_app_info() -> AppInfo { AppInfo { name: "AI Assistant".to_string(), version: env!("CARGO_PKG_VERSION").to_string(), tauri_version: tauri::VERSION.to_string(), } } #[tauri::command] pub async fn open_directory_dialog(app: tauri::AppHandle) -> Result, String> { let (tx, rx) = mpsc::channel(); app.dialog() .file() .set_title("Select Working Directory") .pick_folder(move |path| { let _ = tx.send(path); }); match rx.recv() { Ok(Some(path)) => Ok(Some(path.to_string())), Ok(None) => Ok(None), Err(e) => Err(e.to_string()), } } #[derive(Serialize)] pub struct FileContent { pub path: String, pub content: String, pub size: u64, } #[tauri::command] pub async fn read_local_file(path: String) -> Result { let path_buf = PathBuf::from(&path); if !path_buf.exists() { return Err(format!("File not found: {}", path)); } let metadata = fs::metadata(&path_buf).map_err(|e| e.to_string())?; if metadata.len() > 10 * 1024 * 1024 { return Err("File too large (max 10MB)".to_string()); } let content = fs::read_to_string(&path_buf).map_err(|e| e.to_string())?; Ok(FileContent { path, content, size: metadata.len(), }) } #[derive(Serialize)] pub struct DirectoryEntry { pub name: String, pub path: String, pub is_dir: bool, pub size: u64, } #[tauri::command] pub async fn list_directory(path: String) -> Result, String> { let path_buf = PathBuf::from(&path); if !path_buf.exists() { return Err(format!("Directory not found: {}", path)); } if !path_buf.is_dir() { return Err(format!("Not a directory: {}", path)); } let mut entries = Vec::new(); let read_dir = fs::read_dir(&path_buf).map_err(|e| e.to_string())?; for entry in read_dir { let entry = entry.map_err(|e| e.to_string())?; let metadata = entry.metadata().map_err(|e| e.to_string())?; entries.push(DirectoryEntry { name: entry.file_name().to_string_lossy().to_string(), path: entry.path().to_string_lossy().to_string(), is_dir: metadata.is_dir(), size: metadata.len(), }); } entries.sort_by(|a, b| { if a.is_dir == b.is_dir { a.name.to_lowercase().cmp(&b.name.to_lowercase()) } else if a.is_dir { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater } }); Ok(entries) }