ModuForge-RS 数据转换包
 
 

ModuForge-RS 数据转换包提供了基于不可变数据结构的文档转换系统,支持节点操作、标记管理、属性更新和批量处理。该包是 ModuForge-RS 框架的核心组件,为文档编辑和状态管理提供高效、可靠的转换能力。
🏗️ 架构概述
ModuForge-RS 数据转换包采用基于步骤的转换架构,确保文档变更的可预测性和可追溯性。系统基于以下核心设计原则:
- 步骤驱动: 所有转换操作通过步骤(Step)进行,支持序列化和反序列化
- 延迟计算: 使用延迟计算优化性能,只在需要时重新计算文档状态
- Copy-on-Write: 采用写时复制策略,减少不必要的内存分配
- 事务支持: 完整的提交和回滚机制,支持历史记录管理
- 批量操作: 高效的批量步骤应用,减少中间状态创建
核心架构组件
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Transform     │    │   Step          │    │   Patch         │
│   (转换系统)     │◄──►│   (步骤接口)     │◄──►│   (补丁系统)     │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         ▼                       ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   NodeStep      │    │   AttrStep      │    │   MarkStep      │
│   (节点操作)     │    │   (属性操作)     │    │   (标记操作)     │
└─────────────────┘    └─────────────────┘    └─────────────────┘
🚀 核心功能
1. 转换系统 (Transform)
- 延迟计算: 使用 LazyDoc枚举实现智能的文档状态计算
- 草稿系统: 基于 Tree的草稿状态管理,支持临时修改
- 历史管理: 完整的步骤历史和反向步骤记录
- 批量操作: 高效的批量步骤应用,减少中间状态创建
- 提交回滚: 支持事务提交和回滚操作
2. 步骤系统 (Step)
- 统一接口: 所有转换操作都实现 Step特征
- 序列化支持: 支持步骤的序列化和反序列化
- 反向操作: 自动生成反向步骤,支持撤销操作
- 错误处理: 完善的错误处理和结果反馈机制
3. 节点操作 (NodeStep)
- 添加节点: AddNodeStep支持在指定父节点下添加新节点
- 删除节点: RemoveNodeStep支持删除指定节点及其子树
- 移动节点: MoveNodeStep支持节点在不同父节点间移动
- 递归处理: 自动处理节点的递归结构和子节点关系
4. 属性操作 (AttrStep)
- 属性更新: 支持批量更新节点属性
- 模式验证: 基于 Schema 的属性验证和过滤
- 类型安全: 使用 serde_json::Value确保类型安全
- 增量更新: 支持属性的增量更新操作
5. 标记操作 (MarkStep)
- 添加标记: AddMarkStep支持为节点添加标记
- 删除标记: RemoveMarkStep支持删除指定类型的标记
- 标记验证: 基于 Schema 的标记类型验证
- 批量操作: 支持批量标记操作
6. 补丁系统 (Patch)
- 增量更新: 支持文档的增量更新操作
- 路径定位: 使用路径数组精确定位节点位置
- 操作类型: 支持属性更新、节点操作、标记操作等多种类型
- 序列化: 完整的补丁序列化和反序列化支持
📦 技术栈
核心依赖
[dependencies]
im = { version = "15.1", features = ["serde"] }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
async-trait = "0.1"
anyhow = "1"
thiserror = "2.0.12"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-appender = "0.2"
time = "0.3"
ModuForge-RS 内部依赖
moduforge-model = "0.4.12"
🚀 快速开始
基本使用
use mf_transform::{
    Transform, TransformResult,
    node_step::{AddNodeStep, RemoveNodeStep},
    attr_step::AttrStep,
    mark_step::{AddMarkStep, RemoveMarkStep},
    step::Step
};
use mf_model::{node_type::NodeEnum, schema::Schema, node_pool::NodePool, mark::Mark};
use std::sync::Arc;
use im::HashMap as ImHashMap;
use serde_json::json;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
        let schema = Arc::new(Schema::default());
    let doc = Arc::new(NodePool::default());
    
        let mut transform = Transform::new(doc, schema);
    
        let node_enum = NodeEnum::new("test_node", "paragraph");
    let add_step = Arc::new(AddNodeStep::new(
        "parent_id".to_string(),
        vec![node_enum]
    ));
    transform.step(add_step)?;
    
        let mut attrs = ImHashMap::new();
    attrs.insert("class".to_string(), json!("highlight"));
    let attr_step = Arc::new(AttrStep::new(
        "test_node".to_string(),
        attrs
    ));
    transform.step(attr_step)?;
    
        let mark = Mark::new("bold".to_string(), ImHashMap::new());
    let mark_step = Arc::new(AddMarkStep::new(
        "test_node".to_string(),
        vec![mark]
    ));
    transform.step(mark_step)?;
    
        transform.commit();
    
    println!("转换完成,文档已更新");
    Ok(())
}
批量操作
use mf_transform::{Transform, TransformResult};
use mf_model::{node_type::NodeEnum, schema::Schema, node_pool::NodePool};
use std::sync::Arc;
async fn batch_operations() -> TransformResult<()> {
    let schema = Arc::new(Schema::default());
    let doc = Arc::new(NodePool::default());
    let mut transform = Transform::new(doc, schema);
    
        let mut steps = Vec::new();
    
        for i in 0..5 {
        let node_enum = NodeEnum::new(&format!("node_{}", i), "paragraph");
        let step = Arc::new(AddNodeStep::new(
            "parent_id".to_string(),
            vec![node_enum]
        ));
        steps.push(step);
    }
    
        transform.apply_steps_batch(steps)?;
    
        transform.commit();
    
    println!("批量操作完成,添加了 {} 个节点", transform.history_size());
    Ok(())
}
事务管理
use mf_transform::Transform;
use mf_model::{node_type::NodeEnum, schema::Schema, node_pool::NodePool};
use std::sync::Arc;
async fn transaction_management() -> anyhow::Result<()> {
    let schema = Arc::new(Schema::default());
    let doc = Arc::new(NodePool::default());
    let mut transform = Transform::new(doc, schema);
    
        let node_enum = NodeEnum::new("test_node", "paragraph");
    let step = Arc::new(AddNodeStep::new(
        "parent_id".to_string(),
        vec![node_enum]
    ));
    transform.step(step)?;
    
        if transform.doc_changed() {
        println!("有未提交的更改,历史大小: {}", transform.history_size());
        
                            }
    
    Ok(())
}
自定义步骤
use mf_transform::{step::{Step, StepResult}, TransformResult};
use mf_model::{schema::Schema, tree::Tree};
use std::sync::Arc;
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct CustomStep {
    node_id: String,
    operation: String,
}
impl CustomStep {
    pub fn new(node_id: String, operation: String) -> Self {
        Self { node_id, operation }
    }
}
impl Step for CustomStep {
    fn name(&self) -> String {
        "custom_step".to_string()
    }
    
    fn apply(
        &self,
        tree: &mut Tree,
        _schema: Arc<Schema>,
    ) -> TransformResult<StepResult> {
                match self.operation.as_str() {
            "highlight" => {
                                println!("高亮节点: {}", self.node_id);
                Ok(StepResult::ok())
            }
            "hide" => {
                                println!("隐藏节点: {}", self.node_id);
                Ok(StepResult::ok())
            }
            _ => Ok(StepResult::fail("未知操作".to_string())),
        }
    }
    
    fn serialize(&self) -> Option<Vec<u8>> {
        serde_json::to_vec(self).ok()
    }
    
    fn invert(&self, _tree: &Arc<Tree>) -> Option<Arc<dyn Step>> {
                let reverse_operation = match self.operation.as_str() {
            "highlight" => "unhighlight",
            "hide" => "show",
            _ => return None,
        };
        
        Some(Arc::new(CustomStep::new(
            self.node_id.clone(),
            reverse_operation.to_string(),
        )))
    }
}
🔧 配置选项
转换器配置
use mf_transform::Transform;
use mf_model::{schema::Schema, node_pool::NodePool};
use std::sync::Arc;
let schema = Arc::new(Schema::default());
let doc = Arc::new(NodePool::default());
let mut transform = Transform::new(doc, schema);
transform.set_auto_commit(false);  transform.set_batch_size(100);     
步骤配置
use mf_transform::node_step::AddNodeStep;
use mf_model::node_type::NodeEnum;
let step = AddNodeStep::new(
    "parent_id".to_string(),
    vec![NodeEnum::new("child_node", "paragraph")]
);
step.set_validate(true);      step.set_optimize(true);      
📊 性能特性
延迟计算优化
- 智能计算: 只在需要时重新计算文档状态
- 状态缓存: 缓存已计算的状态,避免重复计算
- 增量更新: 支持增量更新,减少计算开销
内存管理
- Copy-on-Write: 采用写时复制策略,减少内存分配
- 结构共享: 利用不可变数据结构的结构共享特性
- 批量操作: 批量处理减少中间状态创建
并发性能
- 无锁设计: 使用不可变数据结构避免锁竞争
- 原子操作: 基于原子操作的状态管理
- 并发安全: 线程安全的转换操作
🛠️ 错误处理
ModuForge-RS 数据转换包提供了完善的错误处理机制:
use mf_transform::{TransformResult, transform_error};
fn handle_transform_error(result: TransformResult<()>) -> anyhow::Result<()> {
    match result {
        Ok(()) => Ok(()),
        Err(e) => {
                        tracing::error!("转换操作失败: {}", e);
            
                        if e.to_string().contains("node not found") {
                return Err(transform_error("节点不存在").into());
            }
            
            Err(e)
        }
    }
}
常见错误类型
- 节点错误: 节点不存在或操作无效
- 属性错误: 属性验证失败或类型不匹配
- 标记错误: 标记操作失败或类型无效
- 序列化错误: 步骤序列化或反序列化失败
- 验证错误: Schema 验证失败
🧪 测试
单元测试
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_add_node_step() {
        let schema = Arc::new(Schema::default());
        let doc = Arc::new(NodePool::default());
        let mut transform = Transform::new(doc, schema);
        
        let node_enum = NodeEnum::new("test_node", "paragraph");
        let step = Arc::new(AddNodeStep::new(
            "parent_id".to_string(),
            vec![node_enum]
        ));
        
        let result = transform.step(step);
        assert!(result.is_ok());
        assert!(transform.doc_changed());
    }
    
    #[test]
    fn test_attr_step() {
        let schema = Arc::new(Schema::default());
        let doc = Arc::new(NodePool::default());
        let mut transform = Transform::new(doc, schema);
        
        let mut attrs = ImHashMap::new();
        attrs.insert("class".to_string(), json!("test"));
        let step = Arc::new(AttrStep::new(
            "test_node".to_string(),
            attrs
        ));
        
        let result = transform.step(step);
        assert!(result.is_ok());
    }
}
集成测试
#[cfg(test)]
mod integration_tests {
    use super::*;
    
    #[test]
    fn test_complex_transformation() {
        let schema = Arc::new(Schema::default());
        let doc = Arc::new(NodePool::default());
        let mut transform = Transform::new(doc, schema);
        
                let steps = create_complex_steps();
        
        for step in steps {
            let result = transform.step(step);
            assert!(result.is_ok());
        }
        
                transform.commit();
        assert_eq!(transform.history_size(), 5);
    }
}
🔍 监控和调试
性能监控
use mf_transform::Transform;
use std::time::Instant;
async fn monitor_transform_performance(mut transform: Transform) {
    let start = Instant::now();
    
        let steps = create_test_steps();
    for step in steps {
        transform.step(step).unwrap();
    }
    
    let duration = start.elapsed();
    tracing::info!(
        "转换完成 - 步骤数: {}, 耗时: {:?}",
        transform.history_size(),
        duration
    );
}
状态调试
use mf_transform::Transform;
fn debug_transform(transform: &Transform) {
    tracing::debug!("转换器状态:");
    tracing::debug!("  历史大小: {}", transform.history_size());
    tracing::debug!("  文档已更改: {}", transform.doc_changed());
    tracing::debug!("  基础文档: {:?}", transform.base_doc);
}
📚 API 参考
核心类型
- Transform: 主转换器结构体
- Step: 步骤特征,所有转换操作的基础接口
- StepResult: 步骤执行结果
- Patch: 补丁枚举,描述文档修改操作
步骤类型
- AddNodeStep: 添加节点步骤
- RemoveNodeStep: 删除节点步骤
- MoveNodeStep: 移动节点步骤
- AttrStep: 属性更新步骤
- AddMarkStep: 添加标记步骤
- RemoveMarkStep: 删除标记步骤
主要方法
Transform
- new(doc, schema): 创建新转换器
- step(step): 应用单个步骤
- apply_steps_batch(steps): 批量应用步骤
- commit(): 提交更改
- rollback(): 回滚更改
- doc(): 获取当前文档状态
- doc_changed(): 检查文档是否已更改
- history_size(): 获取历史大小
Step
- name(): 获取步骤名称
- apply(tree, schema): 应用步骤
- serialize(): 序列化步骤
- invert(tree): 生成反向步骤
🤝 贡献指南
我们欢迎社区贡献!请查看以下指南:
- 代码风格: 遵循 Rust 标准编码规范
- 测试覆盖: 为新功能添加相应的测试
- 文档更新: 更新相关文档和示例
- 性能考虑: 考虑性能影响和优化
📄 许可证
本项目采用 MIT 许可证 - 查看 LICENSE 文件了解详情。
🔗 相关链接