From b0e463c0ed604af8a7524195f3d01cceae72f002 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 19:06:57 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat=20(core):=20=E9=80=82=E9=85=8D?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=AE=9A=E5=88=B6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/config.rs | 33 +++--- src-tauri/src/main.rs | 196 ++++++++++++++++--------------- src-tauri/src/plugins/manager.rs | 16 +-- src-tauri/src/plugins/mod.rs | 162 ++++++++++++++++++++++++- src-tauri/src/plugins/python2.rs | 40 +++---- src-tauri/src/plugins/python3.rs | 27 +++-- 6 files changed, 310 insertions(+), 164 deletions(-) diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 344008a7..b79ca960 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -1,19 +1,12 @@ +use crate::plugins::PluginConfig; use log::{info, warn}; use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PluginConfig { - pub enabled: bool, // 插件是否启用 - pub execute_home: String, // 插件的执行路径 - pub extensions: Vec, // 插件支持的文件扩展名 - pub language: String, // 插件所属语言 - pub before_compile: String, // 插件在编译前执行的命令 - pub after_compile: String, // 插件在编译完成后执行的命令 - pub run_command: String, // 插件执行的命令 - pub template: String, // 插件的模板 -} +// 全局配置管理器 +use std::sync::Mutex; +use tauri::command; +static CONFIG_MANAGER: Mutex> = Mutex::new(None); #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppConfig { @@ -114,10 +107,6 @@ impl ConfigManager { } } -// 全局配置管理器 -use std::sync::Mutex; -static CONFIG_MANAGER: Mutex> = Mutex::new(None); - // 初始化配置 pub fn init_config() -> Result<(), String> { let config_manager = ConfigManager::new()?; @@ -144,9 +133,6 @@ pub fn get_config_manager() -> Result Result { let guard = get_config_manager()?; @@ -157,6 +143,15 @@ pub async fn get_app_config() -> Result { } } +pub fn get_app_config_internal() -> Result { + let guard = get_config_manager()?; + if let Some(config_manager) = guard.as_ref() { + Ok(config_manager.get_config().clone()) + } else { + Err("配置管理器未初始化".to_string()) + } +} + #[command] pub async fn update_app_config(config: AppConfig) -> Result<(), String> { let mut guard = get_config_manager()?; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e23f671e..d8838964 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -15,7 +15,7 @@ use crate::utils::logger::{ }; use config::{get_app_config, get_config_path, init_config, update_app_config}; -use log::{debug, info}; +use log::{debug, error, info}; use plugins::{CodeExecutionRequest, ExecutionResult, LanguageInfo, PluginManager}; use std::fs; use std::process::{Command, Stdio}; @@ -34,6 +34,7 @@ async fn execute_code( history: State<'_, ExecutionHistory>, plugin_manager: State<'_, PluginManagerState>, ) -> Result { + info!("执行代码 -> 调用插件 [ {} ] 开始", request.language); let manager = plugin_manager.lock().await; let plugin = manager .get_plugin(&request.language) @@ -45,13 +46,16 @@ async fn execute_code( "codeforge_{}_{}.{}", request.language, execution_id, - plugin.get_file_extension() + plugin.get_file_extension().first().unwrap().to_string() )); - // 预处理代码 - let processed_code = plugin - .pre_execute_hook(&request.code) - .map_err(|e| format!("Pre-execution hook failed: {}", e))?; + let processed_code = plugin.pre_execute_hook(&request.code).map_err(|e| { + error!( + "执行代码 -> 调用插件 [ {} ] pre_execute_hook 出现错误 {:?}", + request.language, e + ); + format!("Pre-execution hook failed: {}", e) + })?; // 写入代码到临时文件 fs::write(&file_path, &processed_code) @@ -60,62 +64,62 @@ async fn execute_code( let start_time = std::time::Instant::now(); let mut last_error = String::new(); - // 尝试不同的命令 - for cmd in plugin.get_commands() { - let args = plugin.get_execute_args(file_path.to_str().unwrap()); - debug!( - "执行 {:?} 代码 -> 执行命令: {:?} 携带参数: {:?}", - request.language, cmd, args - ); - - let output = Command::new(cmd) - .args(&args) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output(); - - match output { - Ok(output) => { - let execution_time = start_time.elapsed().as_millis(); - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - - // 清理临时文件 - let _ = fs::remove_file(&file_path); - - let stdout = String::from_utf8_lossy(&output.stdout).to_string(); - let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - - let mut result = ExecutionResult { - success: output.status.success(), - stdout, - stderr, - execution_time, - timestamp, - language: request.language.clone(), - }; - - // 后处理 - let _ = plugin.post_execute_hook(&mut result); - - // 添加到执行历史 - drop(manager); // 释放插件管理器锁 - let mut history_guard = history.lock().await; - history_guard.push(result.clone()); - - // 保持历史记录不超过100条 - if history_guard.len() > 100 { - history_guard.remove(0); - } - - return Ok(result); - } - Err(e) => { - last_error = format!("Failed to execute {} - {}", cmd, e); - continue; + let cmd = plugin.get_command(); + let args = plugin.get_execute_args(file_path.to_str().unwrap()); + info!( + "执行代码 -> 调用插件 [ {} ] 执行命令 {} 携带参数 {}", + request.language, + cmd, + args.join(" ") + ); + + let output = Command::new(&cmd) + .args(&args) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output(); + + match output { + Ok(output) => { + let execution_time = start_time.elapsed().as_millis(); + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + // 清理临时文件 + let _ = fs::remove_file(&file_path); + + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); + + let mut result = ExecutionResult { + success: output.status.success(), + stdout, + stderr, + execution_time, + timestamp, + language: request.language.clone(), + }; + + // 后处理 + let _ = plugin.post_execute_hook(&mut result); + + // 添加到执行历史 + drop(manager); // 释放插件管理器锁 + let mut history_guard = history.lock().await; + history_guard.push(result.clone()); + + // 保持历史记录不超过100条 + if history_guard.len() > 100 { + history_guard.remove(0); } + + info!("执行代码 -> 调用插件 [ {} ] 完成", request.language); + return Ok(result); + } + Err(e) => { + last_error = format!("Failed to execute {} - {}", cmd, e); } } @@ -129,6 +133,7 @@ async fn execute_code( // 清理临时文件 let _ = fs::remove_file(&file_path); + error!("执行代码 -> 调用插件 [ {} ] 失败", request.language); Ok(ExecutionResult { success: false, stdout: String::new(), @@ -137,7 +142,7 @@ async fn execute_code( request.language, request.language, last_error, - plugin.get_commands() + plugin.get_command().to_string() ), execution_time, timestamp, @@ -151,51 +156,52 @@ async fn get_info( language: String, plugin_manager: State<'_, PluginManagerState>, ) -> Result { + info!("获取环境 -> 调用插件 [ {} ] 开始", language); let manager = plugin_manager.lock().await; let plugin = manager .get_plugin(&language) .ok_or_else(|| format!("Unsupported language: {}", language))?; - // 尝试不同的命令 - for cmd in plugin.get_commands() { - debug!("获取插件信息 -> 执行命令: {} 语言: {}", cmd, language); - let version_output = Command::new(cmd).args(plugin.get_version_args()).output(); - - if let Ok(version_out) = version_output { - if version_out.status.success() { - let path_result = Command::new(cmd) - .arg("-c") - .arg(plugin.get_path_command()) - .output(); - - let version = String::from_utf8_lossy(&version_out.stdout) - .trim() - .to_string(); - - let path = if let Ok(path_out) = path_result { - if path_out.status.success() { - String::from_utf8_lossy(&path_out.stdout).trim().to_string() - } else { - "Command found but path unavailable".to_string() - } + let cmd = plugin.get_command(); + debug!("获取环境 -> 插件 [ {} ] 命令 {}", language, cmd); + + let version_output = Command::new(&cmd).args(plugin.get_version_args()).output(); + if let Ok(version_out) = version_output { + if version_out.status.success() { + let path_result = Command::new(&cmd) + .arg("-c") + .arg(plugin.get_path_command()) + .output(); + + let version = String::from_utf8_lossy(&version_out.stdout) + .trim() + .to_string(); + + let path = if let Ok(path_out) = path_result { + if path_out.status.success() { + String::from_utf8_lossy(&path_out.stdout).trim().to_string() } else { - "Path detection failed".to_string() - }; - - return Ok(LanguageInfo { - installed: true, - version, - path, - language: plugin.get_language_name().to_string(), - }); - } + "Command found but path unavailable".to_string() + } + } else { + "Path detection failed".to_string() + }; + + info!("获取环境 -> 调用插件 [ {} ] 完成", language); + return Ok(LanguageInfo { + installed: true, + version, + path, + language: plugin.get_language_name().to_string(), + }); } } + error!("获取环境 -> 调用插件 [ {} ] 失败", language); Ok(LanguageInfo { installed: false, version: "Not found".to_string(), - path: format!("Not found - tried: {:?}", plugin.get_commands()), + path: format!("Not found - tried: {:?}", plugin.get_command()), language: plugin.get_language_name().to_string(), }) } diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index 787641ab..aeeec952 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -53,12 +53,8 @@ impl PluginManager { pub fn get_plugin_info(&self, language: &str) -> Option { self.get_plugin(language).map(|plugin| PluginInfo { name: plugin.get_language_name().to_string(), - file_extension: plugin.get_file_extension().to_string(), - available_commands: plugin - .get_commands() - .iter() - .map(|s| s.to_string()) - .collect(), + file_extension: plugin.get_file_extension().first().unwrap().to_string(), + available_commands: vec![plugin.get_command().to_string()], }) } @@ -68,12 +64,8 @@ impl PluginManager { .values() .map(|plugin| PluginInfo { name: plugin.get_language_name().to_string(), - file_extension: plugin.get_file_extension().to_string(), - available_commands: plugin - .get_commands() - .iter() - .map(|s| s.to_string()) - .collect(), + file_extension: plugin.get_file_extension().first().unwrap().to_string(), + available_commands: vec![plugin.get_command().to_string()] }) .collect() } diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index a0f7f4c0..d8ebde04 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -1,4 +1,7 @@ +use crate::config::get_app_config_internal; +use log::{debug, info}; use serde::{Deserialize, Serialize}; +use std::path::PathBuf; // 通用结构定义 #[derive(Debug, Serialize, Deserialize, Clone)] @@ -25,23 +28,171 @@ pub struct LanguageInfo { pub language: String, } +// 插件配置结构 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PluginConfig { + pub enabled: bool, // 插件是否启用 + pub execute_home: Option, // 插件的执行路径 + pub extensions: Vec, // 插件支持的文件扩展名 + pub language: String, // 插件所属语言 + pub before_compile: Option, // 插件在编译前执行的命令 + pub after_compile: Option, // 插件在编译完成后执行的命令 + pub run_command: Option, // 插件执行的命令,例如 "python2 $filename" + pub template: Option, // 插件的模板 +} + // 语言插件接口 pub trait LanguagePlugin: Send + Sync { + // 获取插件优先级 fn get_order(&self) -> i32 { 0 } + + // 获取插件名称 fn get_language_name(&self) -> &'static str; - fn get_file_extension(&self) -> &'static str; - fn get_commands(&self) -> Vec<&'static str>; + + // 获取插件唯一标记 + fn get_language_key(&self) -> &'static str; + + // 获取插件支持的文件扩展名 + fn get_file_extension(&self) -> Vec { + self.get_config().unwrap().extensions.clone() + } + + // 获取执行目录 + fn get_execute_home(&self) -> Option { + self.get_config() + .and_then(|config| config.execute_home.clone()) + .map(|path| PathBuf::from(path)) + } + + // 获取插件支持的命令 + fn get_command(&self) -> String { + if let Some(config) = self.get_config() { + if let Some(run_cmd) = &config.run_command { + return run_cmd + .split_whitespace() + .next() + .unwrap_or(&config.language) + .to_string(); + } + } + self.get_default_command() + } + + // 获取插件配置 + fn get_config(&self) -> Option { + // 获取全局应用配置 + if let Ok(app_config) = get_app_config_internal() { + // 检查是否有插件配置 + if let Some(ref plugins) = app_config.plugins { + // 根据当前插件的语言名称过滤配置 + let language_name = self.get_language_key(); + + // 查找匹配的插件配置 + let found_config = plugins + .iter() + .find(|config| config.language == language_name) + .cloned(); + + debug!( + "执行代码 -> 获取插件 [ {} ] 配置 {:?}", + language_name, found_config + ); + return found_config; + } + } + + // 如果没有找到配置,返回默认配置 + debug!( + "执行代码 -> 插件 [ {} ] 未找到配置,使用默认配置", + self.get_language_key() + ); + Some(self.get_default_config()) + } + + // 检查插件是否启用 + #[allow(dead_code)] + fn is_enabled(&self) -> bool { + self.get_config() + .map(|config| config.enabled) + .unwrap_or(false) + } + fn get_version_args(&self) -> Vec<&'static str>; fn get_execute_args(&self, file_path: &str) -> Vec; fn get_path_command(&self) -> String; - // 可选的钩子函数 - fn pre_execute_hook(&self, _code: &str) -> Result { - Ok(_code.to_string()) + // 构建默认配置 + fn get_default_config(&self) -> PluginConfig; + + // 获取默认命令 + fn get_default_command(&self) -> String; + + // 预执行钩子 + fn pre_execute_hook(&self, code: &str) -> Result { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 开始", + self.get_language_key() + ); + + if let Some(config) = self.get_config() { + // 1. 执行 before_compile 命令 + if let Some(before_cmd) = &config.before_compile { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 执行 before_compile 命令: {}", + self.get_language_key(), + before_cmd + ); + let output = std::process::Command::new(before_cmd) + .output() + .map_err(|e| { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 执行 before_compile 命令 {} 失败 {:?}", + self.get_language_key(), + before_cmd, + e + ); + + format!("执行 before_compile 失败: {}", e) + })?; + + if !output.status.success() { + return Err(format!( + "before_compile 命令执行失败: {}", + String::from_utf8_lossy(&output.stderr) + )); + } + } + + // 2. 切换到 execute_home 目录 + if let Some(execute_home) = self.get_execute_home() { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 切换到执行目录 {}", + self.get_language_key(), + execute_home.display() + ); + std::env::set_current_dir(&execute_home).map_err(|e| { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 切换到执行目录 {} 失败 {:?}", + self.get_language_key(), + execute_home.display(), + e + ); + format!("切换目录失败: {}", e) + })?; + } + } + + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 结束", + self.get_language_key() + ); + + Ok(code.to_string()) } + // 后执行钩子 fn post_execute_hook(&self, _result: &mut ExecutionResult) -> Result<(), String> { Ok(()) } @@ -51,5 +202,4 @@ pub trait LanguagePlugin: Send + Sync { pub mod manager; pub mod python2; pub mod python3; - pub use manager::PluginManager; diff --git a/src-tauri/src/plugins/python2.rs b/src-tauri/src/plugins/python2.rs index 6e387ea4..51d3034c 100644 --- a/src-tauri/src/plugins/python2.rs +++ b/src-tauri/src/plugins/python2.rs @@ -1,4 +1,5 @@ -use super::{ExecutionResult, LanguagePlugin}; +use super::{LanguagePlugin, PluginConfig}; +use std::vec; pub struct Python2Plugin; @@ -11,12 +12,8 @@ impl LanguagePlugin for Python2Plugin { "Python 2" } - fn get_file_extension(&self) -> &'static str { - "py" - } - - fn get_commands(&self) -> Vec<&'static str> { - vec!["python", "python2"] + fn get_language_key(&self) -> &'static str { + "python2" } fn get_version_args(&self) -> Vec<&'static str> { @@ -31,23 +28,20 @@ impl LanguagePlugin for Python2Plugin { "import sys; print(sys.executable)".to_string() } - fn pre_execute_hook(&self, code: &str) -> Result { - Ok(code.to_string()) - } - - fn post_execute_hook(&self, result: &mut ExecutionResult) -> Result<(), String> { - // Python 特定的后处理 - if result.success && result.stdout.is_empty() && result.stderr.is_empty() { - result.stdout = "代码执行成功 (无输出)".to_string(); - } - - // 清理 Python 特定的错误信息 - if !result.stderr.is_empty() { - result.stderr = result - .stderr - .replace("Traceback (most recent call last):", "Error:"); + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("python2"), + before_compile: None, + extensions: vec![String::from("py")], + execute_home: None, + run_command: Option::from(String::from("python2 $filename")), + after_compile: None, + template: None, } + } - Ok(()) + fn get_default_command(&self) -> String { + self.get_config().unwrap().run_command.unwrap() } } diff --git a/src-tauri/src/plugins/python3.rs b/src-tauri/src/plugins/python3.rs index 55db338b..eb458793 100644 --- a/src-tauri/src/plugins/python3.rs +++ b/src-tauri/src/plugins/python3.rs @@ -1,4 +1,4 @@ -use super::{ExecutionResult, LanguagePlugin}; +use super::{ExecutionResult, LanguagePlugin, PluginConfig}; pub struct Python3Plugin; @@ -11,12 +11,8 @@ impl LanguagePlugin for Python3Plugin { "Python 3" } - fn get_file_extension(&self) -> &'static str { - "py" - } - - fn get_commands(&self) -> Vec<&'static str> { - vec!["python", "python3"] + fn get_language_key(&self) -> &'static str { + "python3" } fn get_version_args(&self) -> Vec<&'static str> { @@ -31,8 +27,21 @@ impl LanguagePlugin for Python3Plugin { "import sys; print(sys.executable)".to_string() } - fn pre_execute_hook(&self, code: &str) -> Result { - Ok(code.to_string()) + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("python3"), + before_compile: None, + extensions: vec![String::from("py")], + execute_home: None, + run_command: Option::from(String::from("python3 $filename")), + after_compile: None, + template: None, + } + } + + fn get_default_command(&self) -> String { + self.get_config().unwrap().run_command.unwrap() } fn post_execute_hook(&self, result: &mut ExecutionResult) -> Result<(), String> { From a6cba845f817cbfb2046a7d84683e5705273fe72 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 22:07:22 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat=20(core):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E9=85=8D=E7=BD=AE=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 + src-tauri/src/config.rs | 72 +++++++-- src-tauri/src/main.rs | 16 +- src-tauri/src/plugins/manager.rs | 15 +- src-tauri/src/plugins/mod.rs | 6 +- src-tauri/src/plugins/python2.rs | 2 +- src-tauri/src/plugins/python3.rs | 2 +- src-tauri/src/setup/menus/app.rs | 6 +- src/components/Settings.vue | 15 +- src/components/setting/Language.vue | 226 ++++++++++++++++++++++++++++ src/types/plugin.ts | 11 ++ src/ui/Tabs.vue | 2 +- 12 files changed, 342 insertions(+), 33 deletions(-) create mode 100644 src/components/setting/Language.vue create mode 100644 src/types/plugin.ts diff --git a/package.json b/package.json index 73471210..1391fe1d 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,14 @@ "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-shell": "^2.3.0", "@vueuse/core": "^13.6.0", + "lodash-es": "^4.17.21", "lucide-vue-next": "^0.539.0", "vue": "^3.5.13" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.11", "@tauri-apps/cli": "^2", + "@types/lodash-es": "^4.17.12", "@vitejs/plugin-vue": "^5.2.1", "autoprefixer": "^10.4.21", "postcss": "^8.5.6", diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index b79ca960..7242f33b 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -1,11 +1,13 @@ use crate::plugins::PluginConfig; +// 全局配置管理器 +use crate::PluginManagerState; use log::{info, warn}; use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; -// 全局配置管理器 use std::sync::Mutex; -use tauri::command; +use tauri::{AppHandle, Manager, command}; + static CONFIG_MANAGER: Mutex> = Mutex::new(None); #[derive(Debug, Clone, Serialize, Deserialize)] @@ -35,9 +37,9 @@ pub struct ConfigManager { } impl ConfigManager { - pub fn new() -> Result { + pub fn new(app_handle: Option<&AppHandle>) -> Result { let config_path = Self::get_config_path()?; - let config = Self::load_config(&config_path)?; + let config = Self::load_config(&config_path, app_handle)?; Ok(Self { config_path, @@ -59,27 +61,65 @@ impl ConfigManager { Ok(config_file) } - fn load_config(config_path: &PathBuf) -> Result { + fn load_config( + config_path: &PathBuf, + app_handle: Option<&AppHandle>, + ) -> Result { + println!("读取配置 -> 正在读取配置文件 {:?}", config_path); if config_path.exists() { match fs::read_to_string(config_path) { Ok(content) => match serde_json::from_str::(&content) { - Ok(config) => { - info!("读取配置 -> 成功加载配置文件: {:?}", config_path); + Ok(mut config) => { + println!("读取配置 -> 成功加载配置文件: {:?}", config_path); + + // 检查 plugins 是否为 null,如果是则加载默认配置 + if config.plugins.is_none() { + println!("读取配置 -> plugins 为 null,加载默认插件配置"); + config.plugins = Self::get_default_plugins_config(app_handle); + } + Ok(config) } Err(e) => { warn!("读取配置 -> 配置文件格式错误,使用默认配置: {}", e); - Ok(AppConfig::default()) + Ok(Self::create_default_config(app_handle)) } }, Err(e) => { warn!("读取配置 -> 读取配置文件失败,使用默认配置: {}", e); - Ok(AppConfig::default()) + Ok(Self::create_default_config(app_handle)) } } } else { - info!("读取配置 -> 配置文件不存在,使用默认配置"); - Ok(AppConfig::default()) + println!("读取配置 -> 配置文件不存在,使用默认配置"); + Ok(Self::create_default_config(app_handle)) + } + } + + fn get_default_plugins_config(app_handle: Option<&AppHandle>) -> Option> { + if let Some(handle) = app_handle { + // 从 Tauri 状态中获取 PluginManager + if let Some(plugin_manager_state) = handle.try_state::() { + // 同步访问插件管理器 + if let Ok(manager) = plugin_manager_state.try_lock() { + return Some(manager.get_all_plugin_default_config()); + } else { + println!("读取配置 -> 无法获取插件管理器锁,使用空配置"); + } + } else { + println!("读取配置 -> 无法获取插件管理器状态,使用空配置"); + } + } + Some(vec![]) + } + + fn create_default_config(app_handle: Option<&AppHandle>) -> AppConfig { + AppConfig { + log_directory: None, + auto_clear_logs: Some(true), + keep_log_days: Some(30), + theme: Some("system".to_string()), + plugins: Self::get_default_plugins_config(app_handle), } } @@ -89,7 +129,7 @@ impl ConfigManager { fs::write(&self.config_path, content).map_err(|e| format!("写入配置文件失败: {}", e))?; - info!("保存配置 -> 配置文件已保存: {:?}", self.config_path); + info!("保存配置 -> 配置文件已保存 {}", self.config_path.display()); Ok(()) } @@ -108,15 +148,15 @@ impl ConfigManager { } // 初始化配置 -pub fn init_config() -> Result<(), String> { - let config_manager = ConfigManager::new()?; +pub fn init_config(app_handle: Option<&AppHandle>) -> Result<(), String> { + let config_manager = ConfigManager::new(app_handle)?; // 如果配置中有自定义日志目录,设置到日志系统 if let Some(log_dir) = config_manager.get_log_directory() { - info!("初始化 -> 从配置文件加载日志目录: {}", log_dir); + println!("读取配置 -> 从配置文件加载日志目录: {}", log_dir); // 使用内部函数设置,避免循环保存 if let Err(e) = crate::logger::set_log_directory_internal(log_dir.to_string()) { - warn!("初始化 -> 应用配置中的日志目录失败: {}", e); + warn!("读取配置 -> 应用配置中的日志目录失败: {}", e); } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index d8838964..03fbd3c1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -46,7 +46,7 @@ async fn execute_code( "codeforge_{}_{}.{}", request.language, execution_id, - plugin.get_file_extension().first().unwrap().to_string() + plugin.get_file_extension() )); let processed_code = plugin.pre_execute_hook(&request.code).map_err(|e| { @@ -62,7 +62,7 @@ async fn execute_code( .map_err(|e| format!("Failed to write temporary file: {}", e))?; let start_time = std::time::Instant::now(); - let mut last_error = String::new(); + let mut last_error: String = String::new(); let cmd = plugin.get_command(); let args = plugin.get_execute_args(file_path.to_str().unwrap()); @@ -162,6 +162,16 @@ async fn get_info( .get_plugin(&language) .ok_or_else(|| format!("Unsupported language: {}", language))?; + plugin.pre_execute_hook(&String::new()).map_err(|e| { + error!( + "获取环境 -> 调用插件 [ {} ] pre_execute_hook 出现错误 {:?}", + language, e + ); + + error!("获取环境 -> 调用插件 [ {} ] 失败", language); + format!("Pre-execution hook failed: {}", e) + })?; + let cmd = plugin.get_command(); debug!("获取环境 -> 插件 [ {} ] 命令 {}", language, cmd); @@ -239,7 +249,7 @@ fn main() { .manage(PluginManagerState::new(PluginManager::new())) .setup(|app| { // 第一步:初始化配置系统 - if let Err(e) = init_config() { + if let Err(e) = init_config(Some(app.handle())) { eprintln!("Failed to initialize config: {}", e); } diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index aeeec952..7ff160d8 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -1,4 +1,4 @@ -use super::{LanguagePlugin, python2::Python2Plugin, python3::Python3Plugin}; +use super::{python2::Python2Plugin, python3::Python3Plugin, LanguagePlugin, PluginConfig}; use std::collections::HashMap; pub struct PluginManager { @@ -53,7 +53,7 @@ impl PluginManager { pub fn get_plugin_info(&self, language: &str) -> Option { self.get_plugin(language).map(|plugin| PluginInfo { name: plugin.get_language_name().to_string(), - file_extension: plugin.get_file_extension().first().unwrap().to_string(), + file_extension: plugin.get_file_extension(), available_commands: vec![plugin.get_command().to_string()], }) } @@ -64,11 +64,18 @@ impl PluginManager { .values() .map(|plugin| PluginInfo { name: plugin.get_language_name().to_string(), - file_extension: plugin.get_file_extension().first().unwrap().to_string(), - available_commands: vec![plugin.get_command().to_string()] + file_extension: plugin.get_file_extension(), + available_commands: vec![plugin.get_command().to_string()], }) .collect() } + + pub fn get_all_plugin_default_config(&self) -> Vec { + self.plugins + .values() + .map(|plugin| plugin.get_default_config()) + .collect() + } } #[derive(Debug, serde::Serialize)] diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index d8ebde04..8372d224 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -33,7 +33,7 @@ pub struct LanguageInfo { pub struct PluginConfig { pub enabled: bool, // 插件是否启用 pub execute_home: Option, // 插件的执行路径 - pub extensions: Vec, // 插件支持的文件扩展名 + pub extension: String, // 插件支持的文件扩展名 pub language: String, // 插件所属语言 pub before_compile: Option, // 插件在编译前执行的命令 pub after_compile: Option, // 插件在编译完成后执行的命令 @@ -55,8 +55,8 @@ pub trait LanguagePlugin: Send + Sync { fn get_language_key(&self) -> &'static str; // 获取插件支持的文件扩展名 - fn get_file_extension(&self) -> Vec { - self.get_config().unwrap().extensions.clone() + fn get_file_extension(&self) -> String { + self.get_config().unwrap().extension.clone() } // 获取执行目录 diff --git a/src-tauri/src/plugins/python2.rs b/src-tauri/src/plugins/python2.rs index 51d3034c..808d09e7 100644 --- a/src-tauri/src/plugins/python2.rs +++ b/src-tauri/src/plugins/python2.rs @@ -33,7 +33,7 @@ impl LanguagePlugin for Python2Plugin { enabled: true, language: String::from("python2"), before_compile: None, - extensions: vec![String::from("py")], + extension: String::from("py"), execute_home: None, run_command: Option::from(String::from("python2 $filename")), after_compile: None, diff --git a/src-tauri/src/plugins/python3.rs b/src-tauri/src/plugins/python3.rs index eb458793..2887bf02 100644 --- a/src-tauri/src/plugins/python3.rs +++ b/src-tauri/src/plugins/python3.rs @@ -32,7 +32,7 @@ impl LanguagePlugin for Python3Plugin { enabled: true, language: String::from("python3"), before_compile: None, - extensions: vec![String::from("py")], + extension: String::from("py"), execute_home: None, run_command: Option::from(String::from("python3 $filename")), after_compile: None, diff --git a/src-tauri/src/setup/menus/app.rs b/src-tauri/src/setup/menus/app.rs index 3a7c31be..899c1ca3 100644 --- a/src-tauri/src/setup/menus/app.rs +++ b/src-tauri/src/setup/menus/app.rs @@ -1,6 +1,7 @@ +use log::info; use tauri::{ - AppHandle, Emitter, - menu::{MenuItemBuilder, Submenu, SubmenuBuilder}, + menu::{MenuItemBuilder, Submenu, SubmenuBuilder}, AppHandle, + Emitter, }; pub fn create_app_submenu(app: &AppHandle) -> tauri::Result> { @@ -38,6 +39,7 @@ pub fn handle_app_menu_event(app: &AppHandle, event_id: &str) { let _event = app.emit("show-settings", ()); } "quit" => { + info!("CodeForge 应用关闭"); app.exit(0); } _ => {} diff --git a/src/components/Settings.vue b/src/components/Settings.vue index 8c1f8f68..c2be0bc9 100644 --- a/src/components/Settings.vue +++ b/src/components/Settings.vue @@ -1,24 +1,33 @@ diff --git a/src/types/plugin.ts b/src/types/plugin.ts new file mode 100644 index 00000000..6aba4e40 --- /dev/null +++ b/src/types/plugin.ts @@ -0,0 +1,11 @@ +export default interface PluginConfig +{ + enabled: boolean // 插件是否启用 + execute_home?: string // 插件的执行路径 + extension: string // 插件支持的文件扩展名 + language: string // 插件所属语言 + before_compile?: string // 插件在编译前执行的命令 + after_compile?: string // 插件在编译完成后执行的命令 + run_command?: string // 插件执行的命令,例如 "python2 $filename" + template?: string // 插件的模板 +} diff --git a/src/ui/Tabs.vue b/src/ui/Tabs.vue index 86243bae..05374e1e 100644 --- a/src/ui/Tabs.vue +++ b/src/ui/Tabs.vue @@ -189,7 +189,7 @@ const headerClasses = computed(() => { baseClasses.push('border-t', 'border-gray-200', 'dark:border-gray-700') } else if (props.position === 'left') { - baseClasses.push('flex-col', 'border-r', 'border-gray-200', 'dark:border-gray-700', 'mr-4', 'border-b-0') + baseClasses.push('flex-col', 'border-gray-200', 'dark:border-gray-700', 'mr-4', 'border-b-0') } else if (props.position === 'right') { baseClasses.push('flex-col', 'border-l', 'border-gray-200', 'dark:border-gray-700', 'ml-4', 'order-2', 'border-b-0') From 9ff82a99026d235d2717475ed532ce5ec513ecb1 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 22:22:07 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix=20(core):=20=E4=BF=AE=E5=A4=8D=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=94=99=E8=AF=AF=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/main.rs | 8 ++++---- src-tauri/src/plugins/manager.rs | 2 +- src-tauri/src/plugins/mod.rs | 2 +- src-tauri/src/setup/menus/app.rs | 4 ++-- src/components/Settings.vue | 3 --- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 03fbd3c1..01a5c9d4 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -62,7 +62,7 @@ async fn execute_code( .map_err(|e| format!("Failed to write temporary file: {}", e))?; let start_time = std::time::Instant::now(); - let mut last_error: String = String::new(); + let mut _last_error: String = String::new(); let cmd = plugin.get_command(); let args = plugin.get_execute_args(file_path.to_str().unwrap()); @@ -119,7 +119,7 @@ async fn execute_code( return Ok(result); } Err(e) => { - last_error = format!("Failed to execute {} - {}", cmd, e); + _last_error = format!("Failed to execute {} - {}", cmd, e); } } @@ -141,7 +141,7 @@ async fn execute_code( "{} interpreter not found. Please install {} and ensure it's in your PATH.\n\nLast error: {}\n\nTried commands: {:?}", request.language, request.language, - last_error, + _last_error, plugin.get_command().to_string() ), execution_time, @@ -162,7 +162,7 @@ async fn get_info( .get_plugin(&language) .ok_or_else(|| format!("Unsupported language: {}", language))?; - plugin.pre_execute_hook(&String::new()).map_err(|e| { + plugin.pre_execute_hook("").map_err(|e| { error!( "获取环境 -> 调用插件 [ {} ] pre_execute_hook 出现错误 {:?}", language, e diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index 7ff160d8..dca12c5a 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -1,4 +1,4 @@ -use super::{python2::Python2Plugin, python3::Python3Plugin, LanguagePlugin, PluginConfig}; +use super::{LanguagePlugin, PluginConfig, python2::Python2Plugin, python3::Python3Plugin}; use std::collections::HashMap; pub struct PluginManager { diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index 8372d224..2b62f8ed 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -63,7 +63,7 @@ pub trait LanguagePlugin: Send + Sync { fn get_execute_home(&self) -> Option { self.get_config() .and_then(|config| config.execute_home.clone()) - .map(|path| PathBuf::from(path)) + .map(PathBuf::from) } // 获取插件支持的命令 diff --git a/src-tauri/src/setup/menus/app.rs b/src-tauri/src/setup/menus/app.rs index 899c1ca3..55b39230 100644 --- a/src-tauri/src/setup/menus/app.rs +++ b/src-tauri/src/setup/menus/app.rs @@ -1,7 +1,7 @@ use log::info; use tauri::{ - menu::{MenuItemBuilder, Submenu, SubmenuBuilder}, AppHandle, - Emitter, + AppHandle, Emitter, + menu::{MenuItemBuilder, Submenu, SubmenuBuilder}, }; pub fn create_app_submenu(app: &AppHandle) -> tauri::Result> { diff --git a/src/components/Settings.vue b/src/components/Settings.vue index c2be0bc9..d288f20d 100644 --- a/src/components/Settings.vue +++ b/src/components/Settings.vue @@ -21,7 +21,6 @@ import Modal from '../ui/Modal.vue' import Tabs from '../ui/Tabs.vue' import General from './setting/General.vue' import Language from './setting/Language.vue' -import { useToast } from '../plugins/toast.ts' const isVisible = ref(false) const activeTab = ref('general') @@ -41,8 +40,6 @@ const closeSettings = () => { }, 300) } -const toast = useToast() - onMounted(async () => { // 延迟显示动画 await nextTick()