| Crates.io | moduforge-model |
| lib.rs | moduforge-model |
| version | 0.5.0 |
| created_at | 2025-04-07 11:09:30.192042+00 |
| updated_at | 2025-08-19 09:19:44.490639+00 |
| description | 不可变数据结构与事务系统基础 |
| homepage | https://github.com/Cassielxd/moduforge-rs |
| repository | https://github.com/Cassielxd/moduforge-rs |
| max_upload_size | |
| id | 1623963 |
| size | 254,654 |
moduforge-model 是 ModuForge 生态系统的核心数据模型模块,提供了完整的文档数据结构和类型系统。该模块基于不可变数据结构设计,支持高性能的文档操作、类型验证和内容匹配。
ModuForge 数据模型采用分层架构设计,每个组件都有明确的职责:
┌─────────────────────────────────────────────────────────────┐
│ Tree │
│ (文档树 + 分片存储 + 缓存优化) │
├─────────────────────────────────────────────────────────────┤
│ NodePool │
│ (节点池 + 内存管理 + 并发安全) │
├─────────────────────────────────────────────────────────────┤
│ Node │
│ (节点定义 + 属性 + 标记) │
├─────────────────────────────────────────────────────────────┤
│ Schema │
│ (模式定义 + 类型验证 + 约束检查) │
├─────────────────────────────────────────────────────────────┤
│ ContentMatch │
│ (内容匹配 + 语法解析 + 状态机) │
└─────────────────────────────────────────────────────────────┘
文件: src/tree.rs
职责: 文档树管理和分片存储
核心特性:
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct Tree {
pub root_id: NodeId,
pub nodes: Vector<im::HashMap<NodeId, Arc<Node>>>, // 分片存储
pub parent_map: im::HashMap<NodeId, NodeId>, // 父子关系映射
num_shards: usize, // 分片数量
}
分片优化:
impl Tree {
// 智能分片索引计算
pub fn get_shard_index(&self, id: &NodeId) -> usize;
// 批量分片索引计算
pub fn get_shard_index_batch<'a>(&self, ids: &'a [&'a NodeId]) -> Vec<(usize, &'a NodeId)>;
// 节点操作
pub fn add(&mut self, parent_id: &NodeId, nodes: Vec<NodeEnum>) -> PoolResult<()>;
pub fn remove_node(&mut self, parent_id: &NodeId, nodes: Vec<NodeId>) -> PoolResult<()>;
pub fn move_node(&mut self, source_parent_id: &NodeId, target_parent_id: &NodeId, node_id: &NodeId, position: Option<usize>) -> PoolResult<()>;
}
文件: src/node.rs
职责: 基础节点定义
im::Vector 的不可变数据结构节点结构:
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Node {
#[serde(rename = "i")]
pub id: NodeId, // 节点ID
#[serde(rename = "t")]
pub r#type: String, // 节点类型
#[serde(rename = "a")]
pub attrs: Attrs, // 节点属性
#[serde(rename = "c")]
pub content: im::Vector<NodeId>, // 子节点列表
#[serde(rename = "m")]
pub marks: im::Vector<Mark>, // 标记列表
}
文件: src/node_type.rs
职责: 节点类型定义和验证
类型定义:
#[derive(Clone, PartialEq, Eq)]
pub struct NodeType {
pub name: String, // 类型名称
pub spec: NodeSpec, // 类型规范
pub desc: String, // 描述信息
pub groups: Vec<String>, // 逻辑分组
pub attrs: HashMap<String, Attribute>, // 属性定义
pub default_attrs: HashMap<String, Value>, // 默认属性
pub content_match: Option<ContentMatch>, // 内容匹配规则
pub mark_set: Option<Vec<MarkType>>, // 支持的标记类型
}
节点规范:
pub struct NodeSpec {
pub content: Option<String>, // 内容约束表达式
pub marks: Option<String>, // 标记类型表达式
pub group: Option<String>, // 逻辑分组
pub desc: Option<String>, // 描述信息
pub attrs: Option<HashMap<String, AttributeSpec>>, // 属性规范
}
文件: src/schema.rs
职责: 文档模式管理
模式定义:
#[derive(Clone, Debug)]
pub struct Schema {
pub spec: SchemaSpec, // 模式规范
pub top_node_type: Option<NodeType>, // 顶级节点类型
pub cached: Arc<Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>>, // 全局缓存
pub nodes: HashMap<String, NodeType>, // 节点类型映射
pub marks: HashMap<String, MarkType>, // 标记类型映射
}
文件: src/content.rs
职责: 内容匹配和语法解析
匹配规则:
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct ContentMatch {
pub next: Vec<MatchEdge>, // 匹配边
pub wrap_cache: Vec<Option<NodeType>>, // 包装缓存
pub valid_end: bool, // 是否有效结束
}
语法支持:
* - 零个或多个+ - 一个或多个? - 零个或一个| - 选择() - 分组{n,m} - 范围文件: src/attrs.rs
职责: 属性系统
im::HashMap 的不可变属性属性定义:
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Attrs {
pub attrs: HashMap<String, Value>,
}
使用示例:
let mut attrs = Attrs::default();
attrs["key1"] = json!("value1");
attrs["key2"] = json!(42);
// 类型安全访问
let value: String = attrs.get_value("key1").unwrap();
文件: src/mark.rs
职责: 标记系统
标记定义:
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Mark {
pub r#type: String, // 标记类型
pub attrs: Attrs, // 标记属性
}
[dependencies]
# 不可变数据结构
im = { workspace = true }
# 序列化
serde = { workspace = true }
serde_json = { workspace = true }
# 并发和同步
parking_lot = { workspace = true }
dashmap = { workspace = true }
crossbeam = { workspace = true }
# 缓存和性能
lru = { workspace = true }
once_cell = "1.19"
# 异步支持
tokio = { workspace = true }
async-trait = { workspace = true }
# 日志和监控
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
tracing-appender = { workspace = true }
# 工具库
nanoid = "0.4.0"
regex = "1.0"
rand = "0.8"
rayon = { workspace = true }
use mf_model::{
Node, Attrs, Mark, Tree, NodeEnum,
node_type::{NodeType, NodeSpec},
schema::{Schema, SchemaSpec}
};
use serde_json::json;
// 1. 创建节点
let attrs = Attrs::from([("level".to_string(), json!(1))].into_iter().collect());
let node = Node::new(
"node1",
"paragraph".to_string(),
attrs,
vec![],
vec![]
);
// 2. 创建文档树
let tree = Tree::new(node);
// 3. 添加子节点
let child_node = Node::new(
"node2",
"text".to_string(),
Attrs::default(),
vec![],
vec![]
);
let node_enum = NodeEnum(child_node, vec![]);
tree.add(&"node1".to_string(), vec![node_enum])?;
// 4. 查询节点
let node = tree.get_node(&"node1".to_string());
let children = tree.children(&"node1".to_string());
use mf_model::schema::{Schema, SchemaSpec, NodeSpec, MarkSpec};
use std::collections::HashMap;
// 1. 定义节点规范
let mut nodes = HashMap::new();
nodes.insert("paragraph".to_string(), NodeSpec {
content: Some("inline*".to_string()),
marks: Some("_".to_string()),
group: Some("block".to_string()),
desc: Some("段落节点".to_string()),
attrs: None,
});
// 2. 定义标记规范
let mut marks = HashMap::new();
marks.insert("strong".to_string(), MarkSpec {
attrs: None,
inclusive: true,
spanning: true,
code: false,
});
// 3. 创建模式规范
let schema_spec = SchemaSpec {
nodes,
marks,
top_node: Some("doc".to_string()),
};
// 4. 编译模式
let schema = Schema::compile(schema_spec)?;
use mf_model::content::ContentMatch;
use mf_model::node_type::NodeType;
// 1. 解析内容表达式
let content_match = ContentMatch::parse(
"paragraph+".to_string(),
&node_types
);
// 2. 验证内容
let nodes = vec![paragraph_node, text_node];
if let Some(result) = content_match.match_fragment(&nodes, &schema) {
if result.valid_end {
println!("内容验证通过");
}
}
// 3. 智能填充
if let Some(needed_types) = content_match.fill(&existing_nodes, true, &schema) {
println!("需要添加的节点类型: {:?}", needed_types);
}
use mf_model::attrs::Attrs;
use serde_json::json;
// 1. 创建属性
let mut attrs = Attrs::default();
attrs["color"] = json!("red");
attrs["size"] = json!(12);
// 2. 类型安全访问
let color: String = attrs.get_value("color").unwrap();
let size: i32 = attrs.get_value("size").unwrap();
// 3. 更新属性
let new_values = [("bold".to_string(), json!(true))].into_iter().collect();
let updated_attrs = attrs.update(new_values);
// 自动分片计算
let shard_index = tree.get_shard_index(&node_id);
// 批量分片操作
let shard_indices = tree.get_shard_index_batch(&node_ids);
// 缓存管理
Tree::clear_shard_cache();
pub mod error_messages {
pub const DUPLICATE_NODE: &str = "重复的节点 ID";
pub const PARENT_NOT_FOUND: &str = "父节点不存在";
pub const CHILD_NOT_FOUND: &str = "子节点不存在";
pub const NODE_NOT_FOUND: &str = "节点不存在";
pub const ORPHAN_NODE: &str = "检测到孤立节点";
pub const INVALID_PARENTING: &str = "无效的父子关系";
pub const CYCLIC_REFERENCE: &str = "检测到循环引用";
pub const CANNOT_REMOVE_ROOT: &str = "无法删除根节点";
}
use mf_model::error::{PoolResult, error_helpers};
fn add_node_safely(tree: &mut Tree, parent_id: &NodeId, node: Node) -> PoolResult<()> {
// 检查父节点是否存在
if !tree.contains_node(parent_id) {
return Err(error_helpers::parent_not_found(parent_id.clone()));
}
// 检查节点ID是否重复
if tree.contains_node(&node.id) {
return Err(error_helpers::duplicate_node(node.id.clone()));
}
// 安全添加节点
tree.add_node(parent_id, &vec![node])
}
# 运行所有测试
cargo test
# 运行特定模块测试
cargo test tree
cargo test content
cargo test schema
# 运行性能测试
cargo test --release
#[test]
fn test_tree_operations() {
let mut tree = Tree::new(create_test_node("root"));
// 测试添加节点
let child = create_test_node("child");
tree.add_node(&"root".to_string(), &vec![child]).unwrap();
// 测试查询节点
assert!(tree.contains_node(&"child".to_string()));
assert_eq!(tree.children_count(&"root".to_string()), 1);
// 测试移动节点
tree.move_node(&"root".to_string(), &"root".to_string(), &"child".to_string(), Some(0)).unwrap();
// 测试删除节点
tree.remove_node(&"root".to_string(), vec!["child".to_string()]).unwrap();
assert!(!tree.contains_node(&"child".to_string()));
}
// 自定义分片数量
let num_shards = std::cmp::max(
std::thread::available_parallelism()
.map(NonZeroUsize::get)
.unwrap_or(2),
2,
);
// 缓存大小配置
static SHARD_INDEX_CACHE: Lazy<RwLock<LruCache<String, usize>>> =
Lazy::new(|| RwLock::new(LruCache::new(NonZeroUsize::new(10000).unwrap())));
// 批量操作优化
let shard_indices = tree.get_shard_index_batch(&node_ids);
// 缓存清理
Tree::clear_shard_cache();
// 内存优化
let compact_tree = tree.compact();
欢迎贡献代码!请确保:
本项目采用 MIT 许可证 - 详见 LICENSE 文件。