// Prevents additional console window on Windows in release #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use tauri::Manager; use tauri::AppHandle; use std::sync::{Arc, Mutex}; use std::collections::HashMap; // IPC Commands #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}! Welcome to ECS Framework Editor.", name) } #[tauri::command] fn open_project(path: String) -> Result { // 项目打开逻辑 Ok(format!("Project opened: {}", path)) } #[tauri::command] fn save_project(path: String, data: String) -> Result<(), String> { // 项目保存逻辑 std::fs::write(&path, data) .map_err(|e| format!("Failed to save project: {}", e))?; Ok(()) } #[tauri::command] fn export_binary(data: Vec, output_path: String) -> Result<(), String> { std::fs::write(&output_path, data) .map_err(|e| format!("Failed to export binary: {}", e))?; Ok(()) } #[tauri::command] async fn open_project_dialog(app: AppHandle) -> Result, String> { use tauri_plugin_dialog::DialogExt; let folder = app.dialog() .file() .set_title("Select Project Directory") .blocking_pick_folder(); Ok(folder.map(|path| path.to_string())) } #[tauri::command] fn scan_directory(path: String, pattern: String) -> Result, String> { use glob::glob; use std::path::Path; let base_path = Path::new(&path); if !base_path.exists() { return Err(format!("Directory does not exist: {}", path)); } let separator = if path.contains('\\') { '\\' } else { '/' }; let glob_pattern = format!("{}{}{}", path.trim_end_matches(&['/', '\\'][..]), separator, pattern); let normalized_pattern = if cfg!(windows) { glob_pattern.replace('/', "\\") } else { glob_pattern.replace('\\', "/") }; let mut files = Vec::new(); match glob(&normalized_pattern) { Ok(entries) => { for entry in entries { match entry { Ok(path) => { if path.is_file() { files.push(path.to_string_lossy().to_string()); } } Err(e) => eprintln!("Error reading entry: {}", e), } } } Err(e) => return Err(format!("Failed to scan directory: {}", e)), } Ok(files) } #[tauri::command] fn read_file_content(path: String) -> Result { std::fs::read_to_string(&path) .map_err(|e| format!("Failed to read file {}: {}", path, e)) } #[derive(serde::Serialize)] struct DirectoryEntry { name: String, path: String, is_dir: bool, } #[tauri::command] fn list_directory(path: String) -> Result, String> { use std::fs; use std::path::Path; let dir_path = Path::new(&path); if !dir_path.exists() { return Err(format!("Directory does not exist: {}", path)); } if !dir_path.is_dir() { return Err(format!("Path is not a directory: {}", path)); } let mut entries = Vec::new(); match fs::read_dir(dir_path) { Ok(read_dir) => { for entry in read_dir { match entry { Ok(entry) => { let entry_path = entry.path(); if let Some(name) = entry_path.file_name() { entries.push(DirectoryEntry { name: name.to_string_lossy().to_string(), path: entry_path.to_string_lossy().to_string(), is_dir: entry_path.is_dir(), }); } } Err(e) => eprintln!("Error reading directory entry: {}", e), } } } Err(e) => return Err(format!("Failed to read directory: {}", e)), } entries.sort_by(|a, b| { match (a.is_dir, b.is_dir) { (true, false) => std::cmp::Ordering::Less, (false, true) => std::cmp::Ordering::Greater, _ => a.name.to_lowercase().cmp(&b.name.to_lowercase()), } }); Ok(entries) } #[tauri::command] fn set_project_base_path( path: String, state: tauri::State>>> ) -> Result<(), String> { let mut paths = state.lock().map_err(|e| format!("Failed to lock state: {}", e))?; paths.insert("current".to_string(), path); Ok(()) } fn main() { let project_paths: Arc>> = Arc::new(Mutex::new(HashMap::new())); let project_paths_clone = Arc::clone(&project_paths); tauri::Builder::default() .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_dialog::init()) .register_uri_scheme_protocol("project", move |_app, request| { let project_paths = Arc::clone(&project_paths_clone); let uri = request.uri(); let path = uri.path(); let file_path = { let paths = project_paths.lock().unwrap(); if let Some(base_path) = paths.get("current") { format!("{}{}", base_path, path) } else { return tauri::http::Response::builder() .status(404) .body(Vec::new()) .unwrap(); } }; match std::fs::read(&file_path) { Ok(content) => { let mime_type = if file_path.ends_with(".ts") || file_path.ends_with(".tsx") { "application/javascript" } else if file_path.ends_with(".js") { "application/javascript" } else if file_path.ends_with(".json") { "application/json" } else { "text/plain" }; tauri::http::Response::builder() .status(200) .header("Content-Type", mime_type) .header("Access-Control-Allow-Origin", "*") .body(content) .unwrap() } Err(e) => { eprintln!("Failed to read file {}: {}", file_path, e); tauri::http::Response::builder() .status(404) .body(Vec::new()) .unwrap() } } }) .setup(move |app| { #[cfg(debug_assertions)] { let window = app.get_webview_window("main").unwrap(); window.open_devtools(); } app.manage(project_paths); Ok(()) }) .invoke_handler(tauri::generate_handler![ greet, open_project, save_project, export_binary, open_project_dialog, scan_directory, read_file_content, list_directory, set_project_base_path ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }