/** * Python环境管理命令 * 用于管理Python环境和依赖 */ use serde::{Deserialize, Serialize}; use tauri::{command, AppHandle}; use std::process::Command; use std::path::PathBuf; #[derive(Debug, Serialize, Deserialize)] pub struct PythonEnvInfo { pub python_type: String, pub python_path: Option, pub version: Option, pub is_available: bool, pub error: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct PythonPackage { pub name: String, pub version: String, pub location: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct PythonEnvStatus { pub embedded: PythonEnvInfo, pub system: PythonEnvInfo, pub current_env: String, pub packages: Vec, } /// 获取嵌入式Python路径 fn get_embedded_python_path() -> Option { let python_exe = if cfg!(target_os = "windows") { "python.exe" } else { "python3" }; let possible_paths = vec![ std::env::current_dir().ok() .map(|p| p.join("src-tauri").join("python-embed").join(python_exe)), std::env::current_exe().ok() .and_then(|p| p.parent().map(|p| p.join("python-embed").join(python_exe))), ]; for path in possible_paths.into_iter().flatten() { if path.exists() { return Some(path); } } None } /// 检查Python环境信息 fn check_python_env(python_path: &str) -> PythonEnvInfo { let mut cmd = Command::new(python_path); cmd.arg("--version"); match cmd.output() { Ok(output) => { if output.status.success() { let version = String::from_utf8_lossy(&output.stdout).trim().to_string(); PythonEnvInfo { python_type: if python_path.contains("python-embed") { "embedded".to_string() } else { "system".to_string() }, python_path: Some(python_path.to_string()), version: Some(version), is_available: true, error: None, } } else { let error = String::from_utf8_lossy(&output.stderr).trim().to_string(); PythonEnvInfo { python_type: if python_path.contains("python-embed") { "embedded".to_string() } else { "system".to_string() }, python_path: Some(python_path.to_string()), version: None, is_available: false, error: Some(error), } } } Err(e) => PythonEnvInfo { python_type: if python_path.contains("python-embed") { "embedded".to_string() } else { "system".to_string() }, python_path: Some(python_path.to_string()), version: None, is_available: false, error: Some(e.to_string()), } } } /// 获取已安装的Python包列表 fn get_installed_packages(python_path: &str) -> Vec { let mut cmd = Command::new(python_path); cmd.args(&["-m", "pip", "list", "--format=json"]); match cmd.output() { Ok(output) => { if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout); match serde_json::from_str::>(&stdout) { Ok(packages) => { packages.into_iter().filter_map(|pkg| { let name = pkg.get("name")?.as_str()?.to_string(); let version = pkg.get("version")?.as_str()?.to_string(); Some(PythonPackage { name, version, location: None, }) }).collect() } Err(_) => Vec::new(), } } else { Vec::new() } } Err(_) => Vec::new(), } } /// 获取Python环境状态 #[command] pub async fn get_python_env_status(_app: AppHandle) -> Result { println!("Getting Python environment status..."); // 检查嵌入式Python let embedded_info = if let Some(embedded_path) = get_embedded_python_path() { check_python_env(&embedded_path.to_string_lossy()) } else { PythonEnvInfo { python_type: "embedded".to_string(), python_path: None, version: None, is_available: false, error: Some("Embedded Python not found".to_string()), } }; // 检查系统Python let system_info = check_python_env("python"); // 确定当前使用的环境 let current_env = if embedded_info.is_available { "embedded".to_string() } else if system_info.is_available { "system".to_string() } else { "none".to_string() }; // 获取当前环境的包列表 let packages = if embedded_info.is_available { if let Some(ref path) = embedded_info.python_path { get_installed_packages(path) } else { Vec::new() } } else if system_info.is_available { get_installed_packages("python") } else { Vec::new() }; Ok(PythonEnvStatus { embedded: embedded_info, system: system_info, current_env, packages, }) } /// 安装Python包 #[command] pub async fn install_python_package(_app: AppHandle, package_name: String) -> Result { println!("Installing Python package: {}", package_name); // 确定使用哪个Python let python_path = if let Some(embedded_path) = get_embedded_python_path() { embedded_path.to_string_lossy().to_string() } else { "python".to_string() }; let mut cmd = Command::new(&python_path); cmd.args(&["-m", "pip", "install", &package_name]); match cmd.output() { Ok(output) => { if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout); Ok(format!("Successfully installed {}: {}", package_name, stdout)) } else { let stderr = String::from_utf8_lossy(&output.stderr); Err(format!("Failed to install {}: {}", package_name, stderr)) } } Err(e) => Err(format!("Failed to execute pip install: {}", e)) } } /// 卸载Python包 #[command] pub async fn uninstall_python_package(_app: AppHandle, package_name: String) -> Result { println!("Uninstalling Python package: {}", package_name); // 确定使用哪个Python let python_path = if let Some(embedded_path) = get_embedded_python_path() { embedded_path.to_string_lossy().to_string() } else { "python".to_string() }; let mut cmd = Command::new(&python_path); cmd.args(&["-m", "pip", "uninstall", "-y", &package_name]); match cmd.output() { Ok(output) => { if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout); Ok(format!("Successfully uninstalled {}: {}", package_name, stdout)) } else { let stderr = String::from_utf8_lossy(&output.stderr); Err(format!("Failed to uninstall {}: {}", package_name, stderr)) } } Err(e) => Err(format!("Failed to execute pip uninstall: {}", e)) } } /// 升级Python包 #[command] pub async fn upgrade_python_package(_app: AppHandle, package_name: String) -> Result { println!("Upgrading Python package: {}", package_name); // 确定使用哪个Python let python_path = if let Some(embedded_path) = get_embedded_python_path() { embedded_path.to_string_lossy().to_string() } else { "python".to_string() }; let mut cmd = Command::new(&python_path); cmd.args(&["-m", "pip", "install", "--upgrade", &package_name]); match cmd.output() { Ok(output) => { if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout); Ok(format!("Successfully upgraded {}: {}", package_name, stdout)) } else { let stderr = String::from_utf8_lossy(&output.stderr); Err(format!("Failed to upgrade {}: {}", package_name, stderr)) } } Err(e) => Err(format!("Failed to execute pip upgrade: {}", e)) } } /// 设置嵌入式Python环境 #[command] pub async fn setup_embedded_python(_app: AppHandle) -> Result { println!("Setting up embedded Python environment..."); // 尝试多个可能的脚本路径 let possible_script_paths = vec![ // 开发环境:从项目根目录 std::env::current_dir().ok() .map(|p| p.join("scripts").join("setup_embedded_python.py")), // 相对路径 Some(std::path::PathBuf::from("scripts/setup_embedded_python.py")), Some(std::path::PathBuf::from("./scripts/setup_embedded_python.py")), // 绝对路径(如果在scripts目录中运行) Some(std::path::PathBuf::from("setup_embedded_python.py")), ]; let mut script_path = None; for path in possible_script_paths.into_iter().flatten() { println!("Checking script path: {:?}", path); if path.exists() { script_path = Some(path); break; } } let script_path = script_path.ok_or_else(|| { let current_dir = std::env::current_dir().unwrap_or_default(); format!( "Setup script not found. Current directory: {:?}. Please ensure scripts/setup_embedded_python.py exists.", current_dir ) })?; println!("Using script path: {:?}", script_path); // 尝试多个Python命令 let python_commands = vec!["python", "python3", "py"]; let mut last_error = String::new(); for python_cmd in python_commands { println!("Trying Python command: {}", python_cmd); let mut cmd = Command::new(python_cmd); cmd.arg(&script_path); cmd.current_dir(std::env::current_dir().unwrap_or_default()); match cmd.output() { Ok(output) => { if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout); return Ok(format!("Embedded Python setup completed using {}: {}", python_cmd, stdout)); } else { let stderr = String::from_utf8_lossy(&output.stderr); last_error = format!("Failed with {}: {}", python_cmd, stderr); println!("{}", last_error); } } Err(e) => { last_error = format!("Failed to execute {} {}: {}", python_cmd, script_path.display(), e); println!("{}", last_error); } } } Err(format!("All Python commands failed. Last error: {}", last_error)) } /// 简化的嵌入式Python设置(使用批处理脚本) #[command] pub async fn setup_embedded_python_simple(_app: AppHandle) -> Result { println!("Setting up embedded Python environment using batch script..."); // 查找批处理脚本 let possible_script_paths = vec![ std::env::current_dir().ok() .map(|p| p.join("scripts").join("setup_embedded_python.bat")), Some(std::path::PathBuf::from("scripts/setup_embedded_python.bat")), Some(std::path::PathBuf::from("./scripts/setup_embedded_python.bat")), ]; let mut script_path = None; for path in possible_script_paths.into_iter().flatten() { println!("Checking batch script path: {:?}", path); if path.exists() { script_path = Some(path); break; } } let script_path = script_path.ok_or_else(|| { let current_dir = std::env::current_dir().unwrap_or_default(); format!( "Batch script not found. Current directory: {:?}. Please ensure scripts/setup_embedded_python.bat exists.", current_dir ) })?; println!("Using batch script: {:?}", script_path); // 在Windows上运行批处理脚本 #[cfg(target_os = "windows")] { let mut cmd = Command::new("cmd"); cmd.args(&["/C", &script_path.to_string_lossy()]); cmd.current_dir(std::env::current_dir().unwrap_or_default()); match cmd.output() { Ok(output) => { let stdout = String::from_utf8_lossy(&output.stdout); let stderr = String::from_utf8_lossy(&output.stderr); if output.status.success() { Ok(format!("Embedded Python setup completed:\nOutput: {}\nErrors: {}", stdout, stderr)) } else { Err(format!("Batch script failed:\nOutput: {}\nErrors: {}", stdout, stderr)) } } Err(e) => Err(format!("Failed to execute batch script: {}", e)) } } // 在非Windows系统上返回错误 #[cfg(not(target_os = "windows"))] { Err("Batch script setup is only available on Windows. Please run the Python script manually.".to_string()) } }