secra_plugins 0.1.32

生产级插件系统 - 插件的生命周期
Documentation
//! 插件卸载相关方法

use crate::error::{PluginManagerError, PluginManagerResult};
use crate::status::ChildPluginInfo;
use super::types::PluginInstance;
use std::collections::HashMap;
use tracing::{debug, error, info, trace, warn};

/// 检查插件依赖关系
///
/// 检查是否有其他插件依赖指定的插件。如果存在依赖关系,则不允许卸载该插件,
/// 以防止破坏依赖链。
///
/// # 参数
/// * `plugins` - 插件实例的映射表(不需要锁,因为通常是在已持有锁的上下文中调用)
/// * `plugin_id` - 要检查的插件ID
///
/// # 返回值
/// * `PluginManagerResult<()>` - 如果没有依赖关系返回 `Ok(())`
///
/// # 错误
/// * `PluginManagerError::DependencyError` - 如果存在其他插件依赖此插件
///
/// # 行为
/// * 遍历所有插件,检查其 `dependencies` 列表
/// * 如果发现任何插件依赖目标插件,收集所有依赖插件的ID
/// * 如果存在依赖关系,返回包含所有依赖插件ID的错误信息
///
/// # 示例
/// ```no_run
/// use std::collections::HashMap;
/// 
/// # fn example() {
/// let plugins = HashMap::new();
/// // 检查是否可以卸载插件
/// check_plugin_dependencies(&plugins, "my-plugin")?;
/// println!("可以安全卸载插件");
/// # }
/// ```
pub fn check_plugin_dependencies(
    plugins: &HashMap<String, PluginInstance>,
    plugin_id: &str,
) -> PluginManagerResult<()> {
    trace!("检查插件依赖关系: {}", plugin_id);
    
    // 查找所有依赖此插件的其他插件
    let dependent_plugins: Vec<String> = plugins
        .iter()
        .filter_map(|(id, instance)| {
            // 检查插件的依赖列表
            if instance.metadata.dependencies.contains(&plugin_id.to_string()) {
                trace!("发现依赖插件 {} 的插件: {}", plugin_id, id);
                Some(id.clone())
            } else {
                None
            }
        })
        .collect();

    if !dependent_plugins.is_empty() {
        let error_msg = format!(
            "无法卸载插件 {},因为以下插件依赖它: {}",
            plugin_id,
            dependent_plugins.join(", ")
        );
        error!("{}", error_msg);
        debug!("依赖插件列表: {:?}", dependent_plugins);
        return Err(PluginManagerError::DependencyError(error_msg));
    }

    debug!("插件 {} 没有依赖关系,可以安全卸载", plugin_id);
    Ok(())
}

/// 清理单个插件的临时文件目录
///
/// 异步删除插件解包时创建的临时文件目录。
/// 这是插件卸载流程的一部分,用于释放磁盘空间。
///
/// # 参数
/// * `plugin_instance` - 要清理的插件实例
///
/// # 行为
/// * 获取插件的临时目录路径
/// * 尝试删除整个临时目录及其所有内容
/// * 如果删除失败,记录警告日志但不抛出错误
/// * 如果删除成功,记录信息日志
///
/// # 注意
/// 此函数不会返回错误,即使清理失败也会继续执行。
/// 这是为了确保卸载流程的健壮性,不会因为文件清理问题而阻塞卸载。
///
/// # 示例
/// ```no_run
/// # async fn example() {
/// // 假设 plugin_instance 是已卸载的插件实例
/// cleanup_single_temp_files(&plugin_instance).await;
/// println!("临时文件清理完成");
/// # }
/// ```
pub async fn cleanup_single_temp_files(plugin_instance: &PluginInstance) {
    let temp_dir = &plugin_instance.plugin_temp_dir;
    trace!("开始清理插件临时文件: {} -> {}", 
           plugin_instance.metadata.id, temp_dir.display());
    
    if let Err(e) = tokio::fs::remove_dir_all(temp_dir).await {
        warn!("清理插件临时目录失败: {} - {}", temp_dir.display(), e);
    } else {
        info!(
            plugin_id = %plugin_instance.metadata.id,
            temp_path = %temp_dir.display(),
            "已清理临时文件: {}",
            temp_dir.display()
        );
        debug!("临时文件清理成功: {}", temp_dir.display());
    }
}