| Crates.io | moduforge-transform |
| lib.rs | moduforge-transform |
| version | 0.5.0 |
| created_at | 2025-04-07 11:10:09.660238+00 |
| updated_at | 2025-08-19 09:21:03.324924+00 |
| description | 不可变数据结构与事务系统基础 |
| homepage | https://github.com/Cassielxd/moduforge-rs |
| repository | https://github.com/Cassielxd/moduforge-rs |
| max_upload_size | |
| id | 1623966 |
| size | 70,091 |
ModuForge-RS 数据转换包提供了基于不可变数据结构的文档转换系统,支持节点操作、标记管理、属性更新和批量处理。该包是 ModuForge-RS 框架的核心组件,为文档编辑和状态管理提供高效、可靠的转换能力。
ModuForge-RS 数据转换包采用基于步骤的转换架构,确保文档变更的可预测性和可追溯性。系统基于以下核心设计原则:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Transform │ │ Step │ │ Patch │
│ (转换系统) │◄──►│ (步骤接口) │◄──►│ (补丁系统) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ NodeStep │ │ AttrStep │ │ MarkStep │
│ (节点操作) │ │ (属性操作) │ │ (标记操作) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
LazyDoc 枚举实现智能的文档状态计算Tree 的草稿状态管理,支持临时修改Step 特征AddNodeStep 支持在指定父节点下添加新节点RemoveNodeStep 支持删除指定节点及其子树MoveNodeStep 支持节点在不同父节点间移动serde_json::Value 确保类型安全AddMarkStep 支持为节点添加标记RemoveMarkStep 支持删除指定类型的标记[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-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<()> {
// 创建文档和 Schema
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());
// 可以选择提交或回滚
// transform.commit(); // 提交更改
// transform.rollback(); // 回滚更改
}
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); // 启用优化
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)
}
}
}
#[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);
}
Transform: 主转换器结构体Step: 步骤特征,所有转换操作的基础接口StepResult: 步骤执行结果Patch: 补丁枚举,描述文档修改操作AddNodeStep: 添加节点步骤RemoveNodeStep: 删除节点步骤MoveNodeStep: 移动节点步骤AttrStep: 属性更新步骤AddMarkStep: 添加标记步骤RemoveMarkStep: 删除标记步骤new(doc, schema): 创建新转换器step(step): 应用单个步骤apply_steps_batch(steps): 批量应用步骤commit(): 提交更改rollback(): 回滚更改doc(): 获取当前文档状态doc_changed(): 检查文档是否已更改history_size(): 获取历史大小name(): 获取步骤名称apply(tree, schema): 应用步骤serialize(): 序列化步骤invert(tree): 生成反向步骤我们欢迎社区贡献!请查看以下指南:
本项目采用 MIT 许可证 - 查看 LICENSE 文件了解详情。