secra_plugins 0.1.32

生产级插件系统 - 插件的生命周期
Documentation
//! 插件执行相关操作

use crate::error::{PluginManagerError, PluginManagerResult};
use crate::manager::types::PluginMap;
use crate::manager::executor::validate_execute_params;
use crate::manager::validation::sanitize_plugin_name;
use tokio::time::{timeout, Duration};
use tracing::{debug, error, info, trace, warn};

/// 执行插件功能
///
/// 调用指定插件的 `execute` 方法来执行特定的功能操作。插件必须处于运行状态才能执行。
/// 执行过程会进行参数验证、状态检查和超时保护。
///
/// # 参数
/// * `plugins` - 插件实例的共享映射表,使用 `Arc<RwLock<>>` 保证线程安全
/// * `plugin_id` - 要执行功能的插件ID
/// * `action` - 要执行的动作名称,会被验证长度和格式
/// * `params` - 传递给插件执行的参数,使用 JSON 格式
/// * `timeout_secs` - 执行操作的超时时间(秒)
///
/// # 返回值
/// * `PluginManagerResult<serde_json::Value>` - 成功时返回插件执行的结果(JSON 格式)
///
/// # 错误
/// * `PluginManagerError::NotFound` - 如果指定的插件不存在
/// * `PluginManagerError::StateError` - 如果插件未处于运行状态
/// * `PluginManagerError::ValidationFailed` - 如果动作名称验证失败
/// * `PluginManagerError::ExecutionError` - 如果插件执行失败或超时
///
/// # 执行流程
/// 1. 验证并清理插件ID
/// 2. 验证动作名称参数
/// 3. 检查插件是否存在且处于运行状态
/// 4. 调用插件的 `execute` 方法(带超时保护)
/// 5. 返回执行结果或错误信息
///
/// # 示例
/// ```no_run
/// use std::sync::Arc;
/// use tokio::sync::RwLock;
/// use std::collections::HashMap;
/// use serde_json::json;
/// 
/// # async fn example() {
/// let plugins = Arc::new(RwLock::new(HashMap::new()));
/// let params = json!({"key": "value"});
/// let result = execute_plugin(plugins, "my-plugin", "my_action", params, 30).await?;
/// println!("执行结果: {}", result);
/// # }
/// ```
pub async fn execute_plugin(
    plugins: PluginMap,
    plugin_id: &str,
    action: &str,
    params: serde_json::Value,
    timeout_secs: u64,
) -> PluginManagerResult<serde_json::Value> {
    trace!("开始执行插件功能 - 插件: {}, 动作: {}, 超时: {}s", 
           plugin_id, action, timeout_secs);
    
    let sanitized_plugin_id = sanitize_plugin_name(plugin_id)?;
    debug!("插件ID清理: {} -> {}", plugin_id, sanitized_plugin_id);

    validate_execute_params(action)?;
    debug!("执行参数验证通过");

    // 合并锁获取:一次性完成验证和执行,减少锁竞争
    // 注意:execute 是异步的,会在锁内执行,但使用超时保护避免长时间阻塞
    let execute_result = {
        trace!("获取读锁,查找插件实例");
        let plugins = plugins.read().await;

        let plugin_instance = plugins.get(&sanitized_plugin_id)
            .ok_or_else(|| {
                error!("插件不存在: {}", plugin_id);
                PluginManagerError::not_found(plugin_id)
            })?;
        
        debug!("找到插件实例: {} (状态: {:?})", 
               plugin_instance.metadata.name, plugin_instance.status);

        // 验证插件状态
        if !plugin_instance.status.is_running() {
            warn!("插件 {} 未运行,当前状态: {}", plugin_id, plugin_instance.status);
            return Err(PluginManagerError::StateError(
                format!(
                    "插件 {} 未运行,当前状态: {}",
                    plugin_id,
                    plugin_instance.status
                )
            ));
        }

        // 在锁内获取日志信息(避免克隆)
        let plugin_name = &plugin_instance.metadata.name;
        
        // 记录日志(在锁内,但日志操作很快,不会显著影响性能)
        info!(
            plugin_id = %sanitized_plugin_id,
            plugin_name = %plugin_name,
            action = %action,
            timeout = timeout_secs,
            "执行插件功能"
        );
        trace!("开始调用插件 execute 方法");

        // 在锁内执行插件功能(使用超时保护)
        // 这是必要的,因为我们需要确保插件在执行期间不被卸载
        timeout(
            Duration::from_secs(timeout_secs),
            plugin_instance.plugin.execute(action, params)
        ).await
    };

    match execute_result {
        Ok(Ok(result)) => {
            info!(
                plugin_id = %sanitized_plugin_id,
                action = %action,
                "插件功能执行成功"
            );
            trace!("插件功能执行完成,返回结果");
            Ok(result)
        }
        Ok(Err(e)) => {
            error!(
                plugin_id = %sanitized_plugin_id,
                action = %action,
                error = %e,
                "插件功能执行失败"
            );
            Err(PluginManagerError::ExecutionError(
                format!("插件 {} 执行失败: {}", plugin_id, e)
            ))
        }
        Err(_) => {
            error!(
                plugin_id = %sanitized_plugin_id,
                action = %action,
                timeout = timeout_secs,
                "插件功能执行超时"
            );
            Err(PluginManagerError::ExecutionError(
                format!(
                    "插件 {} 执行超时(超时时间: {}秒)",
                    plugin_id,
                    timeout_secs
                )
            ))
        }
    }
}