Skip to content

ModuForge-RS 插件开发完整指南

概述

ModuForge-RS 的插件系统基于三个核心组件:

  • Resource: 插件状态数据
  • StateField: 状态管理器
  • PluginTrait: 插件行为定义

1. 基础插件结构

第一步:定义插件状态资源

rust
use moduforge_state::resource::Resource;
use std::borrow::Cow;

#[derive(Debug, Clone)]
pub struct MyPluginState {
    pub counter: u64,
    pub settings: im::HashMap<String, String>,
    pub active: bool,
}

impl Resource for MyPluginState {
    fn name(&self) -> Cow<str> {
        "MyPluginState".into()
    }
}

impl MyPluginState {
    pub fn new() -> Self {
        Self {
            counter: 0,
            settings: im::HashMap::new(),
            active: true,
        }
    }
    
    pub fn increment(&mut self) {
        self.counter += 1;
    }
    
    pub fn set_setting(&mut self, key: String, value: String) {
        self.settings.insert(key, value);
    }
}

第二步:实现状态字段管理器

rust
use moduforge_state::{
    plugin::StateField,
    resource::Resource,
    state::{State, StateConfig},
    transaction::Transaction,
};
use async_trait::async_trait;
use std::sync::Arc;

#[derive(Debug)]
pub struct MyStateField;

#[async_trait]
impl StateField for MyStateField {
    // 初始化插件状态
    async fn init(
        &self,
        _config: &StateConfig,
        _instance: Option<&State>,
    ) -> Arc<dyn Resource> {
        println!("🔧 初始化我的插件状态");
        Arc::new(MyPluginState::new())
    }

    // 处理状态变更
    async fn apply(
        &self,
        tr: &Transaction,
        value: Arc<dyn Resource>,
        _old_state: &State,
        _new_state: &State,
    ) -> Arc<dyn Resource> {
        // 尝试向下转型为具体的状态类型
        if let Some(state) = value.downcast_arc::<MyPluginState>() {
            let mut new_state = (**state).clone();
            
            // 根据事务元数据更新状态
            if let Some(action) = tr.get_meta::<String>("action") {
                match action.as_str() {
                    "increment_counter" => {
                        new_state.increment();
                        println!("📈 计数器更新: {}", new_state.counter);
                    }
                    "set_plugin_setting" => {
                        if let Some(key) = tr.get_meta::<String>("setting_key") {
                            if let Some(val) = tr.get_meta::<String>("setting_value") {
                                new_state.set_setting(
                                    key.as_str().to_string(), 
                                    val.as_str().to_string()
                                );
                                println!("⚙️ 设置更新: {} = {}", key.as_str(), val.as_str());
                            }
                        }
                    }
                    "toggle_plugin" => {
                        new_state.active = !new_state.active;
                        println!("🔄 插件状态: {}", if new_state.active { "激活" } else { "停用" });
                    }
                    _ => {}
                }
            }
            
            Arc::new(new_state)
        } else {
            // 如果类型转换失败,返回原状态
            value
        }
    }

    // 可选:序列化状态
    fn serialize(&self, value: Arc<dyn Resource>) -> Option<Vec<u8>> {
        if let Some(state) = value.downcast_arc::<MyPluginState>() {
            serde_json::to_vec(&**state).ok()
        } else {
            None
        }
    }
    
    // 可选:反序列化状态
    fn deserialize(&self, data: &Vec<u8>) -> Option<Arc<dyn Resource>> {
        serde_json::from_slice::<MyPluginState>(data)
            .ok()
            .map(|state| Arc::new(state) as Arc<dyn Resource>)
    }
}

第三步:实现插件行为

rust
use moduforge_state::{
    plugin::PluginTrait,
    transaction::Transaction,
    state::State,
    error::StateResult,
};
use async_trait::async_trait;

#[derive(Debug)]
pub struct MyPlugin;

#[async_trait]
impl PluginTrait for MyPlugin {
    // 事务后处理:生成额外的事务
    async fn append_transaction(
        &self,
        transactions: &[Transaction],
        _old_state: &State,
        new_state: &State,
    ) -> StateResult<Option<Transaction>> {
        // 检查传入的事务
        for tr in transactions {
            if let Some(action) = tr.get_meta::<String>("action") {
                match action.as_str() {
                    "document_created" => {
                        // 当创建文档时,自动增加计数器
                        let mut counter_tr = Transaction::new(new_state);
                        counter_tr.set_meta("generated_by", "my_plugin");
                        counter_tr.set_meta("action", "increment_counter");
                        counter_tr.set_meta("reason", "document_created");
                        
                        println!("📄 检测到文档创建,自动增加计数器");
                        return Ok(Some(counter_tr));
                    }
                    "user_login" => {
                        // 用户登录时,记录设置
                        if let Some(username) = tr.get_meta::<String>("username") {
                            let mut setting_tr = Transaction::new(new_state);
                            setting_tr.set_meta("generated_by", "my_plugin");
                            setting_tr.set_meta("action", "set_plugin_setting");
                            setting_tr.set_meta("setting_key", "last_user");
                            setting_tr.set_meta("setting_value", username.as_ptr().clone());
                            
                            println!("👤 用户登录,记录最后用户: {}", username.as_str());
                            return Ok(Some(setting_tr));
                        }
                    }
                    _ => {}
                }
            }
        }
        
        Ok(None)
    }

    // 事务过滤:决定是否允许事务执行
    async fn filter_transaction(
        &self,
        transaction: &Transaction,
        state: &State,
    ) -> bool {
        // 获取插件状态
        if let Some(plugin_state) = self.get_plugin_state(state) {
            // 如果插件被停用,拒绝某些操作
            if !plugin_state.active {
                if let Some(action) = transaction.get_meta::<String>("action") {
                    match action.as_str() {
                        "sensitive_operation" => {
                            println!("🚫 插件已停用,拒绝敏感操作");
                            return false;
                        }
                        _ => {}
                    }
                }
            }
        }
        
        // 默认允许所有事务
        true
    }
}

impl MyPlugin {
    // 辅助方法:获取插件状态
    fn get_plugin_state(&self, state: &State) -> Option<Arc<MyPluginState>> {
        state.get_field("my_plugin.v1")
            .and_then(|resource| resource.downcast_arc::<MyPluginState>())
    }
}

第四步:组装插件

rust
use moduforge_core::{extension::Extension, types::Extensions};
use moduforge_state::plugin::{Plugin, PluginSpec};
use std::sync::Arc;

pub fn create_my_plugin_extension() -> Extension {
    let mut extension = Extension::new();
    
    // 创建插件规格
    let plugin_spec = PluginSpec {
        key: ("my_plugin".to_string(), "v1".to_string()),
        state_field: Some(Arc::new(MyStateField)),
        tr: Some(Arc::new(MyPlugin)),
        priority: 10, // 优先级:数字越小优先级越高
    };
    
    // 创建插件实例
    let plugin = Plugin::new(plugin_spec);
    
    // 添加到扩展
    extension.add_plugin(Arc::new(plugin));
    
    extension
}

2. 在编辑器中使用插件

rust
use moduforge_core::{
    EditorResult,
    runtime::Editor,
    types::{EditorOptionsBuilder, Extensions},
};

async fn create_editor_with_my_plugin() -> EditorResult<Editor> {
    let options = EditorOptionsBuilder::new()
        .add_extension(Extensions::E(create_my_plugin_extension()))
        .history_limit(100)
        .build();
        
    let editor = Editor::create(options).await?;
    Ok(editor)
}

// 使用示例
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // 初始化日志
    moduforge_state::init_logging("info", None)?;
    
    // 创建编辑器
    let mut editor = create_editor_with_my_plugin().await?;
    
    // 触发插件行为的事务
    let mut tr = editor.get_tr();
    tr.set_meta("action", "document_created");
    tr.set_meta("document_title", "测试文档");
    editor.dispatch(tr).await?;
    
    // 手动触发计数器增加
    let mut tr2 = editor.get_tr();
    tr2.set_meta("action", "increment_counter");
    editor.dispatch(tr2).await?;
    
    // 设置插件配置
    let mut tr3 = editor.get_tr();
    tr3.set_meta("action", "set_plugin_setting");
    tr3.set_meta("setting_key", "theme");
    tr3.set_meta("setting_value", "dark");
    editor.dispatch(tr3).await?;
    
    println!("✅ 插件演示完成");
    Ok(())
}

3. 高级插件模式

插件间通信

rust
#[derive(Debug)]
pub struct CommunicationPlugin;

#[async_trait]
impl PluginTrait for CommunicationPlugin {
    async fn append_transaction(
        &self,
        transactions: &[Transaction],
        _old_state: &State,
        new_state: &State,
    ) -> StateResult<Option<Transaction>> {
        for tr in transactions {
            if let Some(sender) = tr.get_meta::<String>("sender_plugin") {
                if sender.as_str() == "my_plugin" {
                    // 响应来自其他插件的消息
                    let mut response_tr = Transaction::new(new_state);
                    response_tr.set_meta("generated_by", "communication_plugin");
                    response_tr.set_meta("action", "plugin_message_received");
                    response_tr.set_meta("from", sender.as_ptr().clone());
                    
                    return Ok(Some(response_tr));
                }
            }
        }
        Ok(None)
    }
}

资源共享插件

rust
#[derive(Debug)]
pub struct ResourceSharingPlugin;

impl ResourceSharingPlugin {
    // 向全局资源管理器添加共享资源
    pub fn setup_shared_resources(
        resource_manager: &moduforge_state::ops::GlobalResourceManager
    ) -> moduforge_core::EditorResult<()> {
        // 添加共享缓存
        let shared_cache = MySharedCache::new();
        resource_manager.resource_table.add(shared_cache);
        
        // 添加配置管理器
        let config_manager = ConfigManager::new();
        resource_manager.resource_table.add(config_manager);
        
        Ok(())
    }
}

// 在扩展中添加资源设置函数
pub fn create_resource_sharing_extension() -> Extension {
    let mut extension = Extension::new();
    
    // 添加资源设置操作
    extension.add_op_fn(Arc::new(|resource_manager| {
        ResourceSharingPlugin::setup_shared_resources(resource_manager)
    }));
    
    // 添加插件...
    
    extension
}

4. 测试插件

rust
#[cfg(test)]
mod tests {
    use super::*;
    use moduforge_core::{runtime::Editor, types::EditorOptionsBuilder};
    
    #[tokio::test]
    async fn test_plugin_counter() {
        let mut editor = create_editor_with_my_plugin().await.unwrap();
        
        // 触发计数器增加
        let mut tr = editor.get_tr();
        tr.set_meta("action", "increment_counter");
        editor.dispatch(tr).await.unwrap();
        
        // 验证状态
        let state = editor.get_state();
        let plugin_state = state.get_field("my_plugin.v1")
            .unwrap()
            .downcast_arc::<MyPluginState>()
            .unwrap();
            
        assert_eq!(plugin_state.counter, 1);
    }
    
    #[tokio::test]
    async fn test_plugin_settings() {
        let mut editor = create_editor_with_my_plugin().await.unwrap();
        
        // 设置配置
        let mut tr = editor.get_tr();
        tr.set_meta("action", "set_plugin_setting");
        tr.set_meta("setting_key", "test_key");
        tr.set_meta("setting_value", "test_value");
        editor.dispatch(tr).await.unwrap();
        
        // 验证设置
        let state = editor.get_state();
        let plugin_state = state.get_field("my_plugin.v1")
            .unwrap()
            .downcast_arc::<MyPluginState>()
            .unwrap();
            
        assert_eq!(
            plugin_state.settings.get("test_key").unwrap(),
            "test_value"
        );
    }
}

5. 最佳实践

错误处理

rust
#[async_trait]
impl StateField for MyStateField {
    async fn apply(
        &self,
        tr: &Transaction,
        value: Arc<dyn Resource>,
        _old_state: &State,
        _new_state: &State,
    ) -> Arc<dyn Resource> {
        if let Some(state) = value.downcast_arc::<MyPluginState>() {
            let mut new_state = (**state).clone();
            
            // 安全的元数据访问
            if let Some(action) = tr.get_meta::<String>("action") {
                match action.as_str() {
                    "risky_operation" => {
                        // 添加错误检查
                        if let Some(param) = tr.get_meta::<i32>("param") {
                            if **param > 0 {
                                new_state.counter += **param as u64;
                            } else {
                                eprintln!("⚠️ 无效参数: {}", **param);
                            }
                        }
                    }
                    _ => {}
                }
            }
            
            Arc::new(new_state)
        } else {
            // 始终返回有效状态
            eprintln!("❌ 状态类型转换失败");
            value
        }
    }
}

性能优化

rust
// 使用 lazy_static 缓存重复计算
lazy_static::lazy_static! {
    static ref PLUGIN_CONFIG: im::HashMap<String, String> = {
        let mut config = im::HashMap::new();
        config.insert("cache_size".to_string(), "1000".to_string());
        config.insert("timeout".to_string(), "5000".to_string());
        config
    };
}

// 减少克隆操作
#[async_trait]
impl StateField for OptimizedStateField {
    async fn apply(
        &self,
        tr: &Transaction,
        value: Arc<dyn Resource>,
        _old_state: &State,
        _new_state: &State,
    ) -> Arc<dyn Resource> {
        if let Some(state) = value.downcast_arc::<MyPluginState>() {
            // 只在需要时才克隆
            if tr.get_meta::<String>("action").is_some() {
                let mut new_state = (**state).clone();
                // 处理变更...
                Arc::new(new_state)
            } else {
                // 无变更时直接返回原状态
                value
            }
        } else {
            value
        }
    }
}

这个完整的插件开发指南提供了从基础到高级的所有必要信息,让开发者能够成功创建和使用 ModuForge-RS 插件。