use std::collections::HashMap;
use serde_json::Value;
use crate::{
error::{error_helpers::schema_error, PoolResult},
id_generator::IdGenerator,
mark::Mark,
mark_definition::MarkDefinition,
node::Node,
node_definition::{NodeDefinition, NodeTree},
schema::Schema,
types::NodeId,
};
#[derive(Clone)]
pub struct NodeFactory<'schema> {
schema: &'schema Schema,
}
impl<'schema> NodeFactory<'schema> {
pub fn new(schema: &'schema Schema) -> Self {
Self { schema }
}
pub fn schema(&self) -> &'schema Schema {
self.schema
}
pub fn create_node(
&self,
type_name: &str,
id: Option<NodeId>,
attrs: Option<&HashMap<String, Value>>,
content: Vec<NodeId>,
marks: Option<Vec<Mark>>,
) -> PoolResult<Node> {
let node_type = self.schema.nodes.get(type_name).ok_or_else(|| {
schema_error(&format!("无法在 schema 中找到节点类型:{type_name}"))
})?;
Ok(Self::instantiate_node(node_type, id, attrs, content, marks))
}
pub fn node_definition(
&self,
type_name: &str,
) -> Option<&NodeDefinition> {
self.schema.nodes.get(type_name)
}
pub fn create_mark(
&self,
type_name: &str,
attrs: Option<&HashMap<String, Value>>,
) -> PoolResult<Mark> {
let mark_def = self.schema.marks.get(type_name).ok_or_else(|| {
schema_error(&format!("无法在 schema 中找到标记类型:{type_name}"))
})?;
Ok(Self::instantiate_mark(mark_def, attrs))
}
pub fn node_names(&self) -> Vec<&'schema str> {
let mut names: Vec<&'schema str> =
self.schema.nodes.keys().map(|key| key.as_str()).collect();
names.sort();
names
}
pub fn mark_names(&self) -> Vec<&'schema str> {
let mut names: Vec<&'schema str> =
self.schema.marks.keys().map(|key| key.as_str()).collect();
names.sort();
names
}
pub fn ensure_node(
&self,
type_name: &str,
) -> PoolResult<&NodeDefinition> {
match self.schema.nodes.get(type_name) {
Some(def) => Ok(def),
None => {
let mut available: Vec<&String> =
self.schema.nodes.keys().collect();
available.sort_by(|a, b| a.as_str().cmp(b.as_str()));
Err(schema_error(&self.missing_message(
"节点类型",
type_name,
available,
)))
},
}
}
pub fn ensure_mark(
&self,
type_name: &str,
) -> PoolResult<&MarkDefinition> {
match self.schema.marks.get(type_name) {
Some(def) => Ok(def),
None => {
let mut available: Vec<&String> =
self.schema.marks.keys().collect();
available.sort_by(|a, b| a.as_str().cmp(b.as_str()));
Err(schema_error(&self.missing_message(
"标记类型",
type_name,
available,
)))
},
}
}
fn missing_message(
&self,
kind: &str,
name: &str,
available: Vec<&String>,
) -> String {
if available.is_empty() {
format!("未找到{kind} \"{name}\"。当前 Schema 中未声明任何{kind}。")
} else {
let preview: Vec<&str> =
available.iter().take(5).map(|s| s.as_str()).collect();
format!(
"未找到{kind} \"{name}\"。可用的{kind}示例:{}",
preview.join(", ")
)
}
}
pub fn mark_definition(
&self,
type_name: &str,
) -> Option<&MarkDefinition> {
self.schema.marks.get(type_name)
}
pub fn definitions(
&self
) -> (&HashMap<String, NodeDefinition>, &HashMap<String, MarkDefinition>)
{
(&self.schema.nodes, &self.schema.marks)
}
pub fn create_top_node(
&self,
id: Option<NodeId>,
attrs: Option<&HashMap<String, Value>>,
content: Vec<Node>,
marks: Option<Vec<Mark>>,
) -> PoolResult<NodeTree> {
let top_node_type = self
.schema
.top_node_type
.as_ref()
.ok_or_else(|| schema_error("未找到顶级节点类型定义"))?;
self.create_tree_with_type(top_node_type, id, attrs, content, marks)
}
pub fn create_tree(
&self,
type_name: &str,
id: Option<NodeId>,
attrs: Option<&HashMap<String, Value>>,
content: Vec<Node>,
marks: Option<Vec<Mark>>,
) -> PoolResult<NodeTree> {
let node_type = self.schema.nodes.get(type_name).ok_or_else(|| {
schema_error(&format!("无法在 schema 中找到节点类型:{type_name}"))
})?;
self.create_tree_with_type(node_type, id, attrs, content, marks)
}
pub(crate) fn create_tree_with_type(
&self,
node_type: &NodeDefinition,
id: Option<NodeId>,
attrs: Option<&HashMap<String, Value>>,
content: Vec<Node>,
marks: Option<Vec<Mark>>,
) -> PoolResult<NodeTree> {
let id: NodeId = id.unwrap_or_else(IdGenerator::get_id);
let computed_attrs = node_type.compute_attrs(attrs);
let computed_marks = node_type.compute_marks(marks);
let mut filled_nodes: Vec<NodeTree> = Vec::new();
let mut final_content_ids: Vec<NodeId> = Vec::new();
if let Some(content_match) = &node_type.content_match {
if let Some(matched) =
content_match.match_fragment(&content, self.schema)
{
if let Some(needed_type_names) =
matched.fill(&content, true, self.schema)
{
for type_name in needed_type_names {
if let Some(existing_node) =
content.iter().find(|n| n.r#type == type_name)
{
let attrs_map: HashMap<String, Value> =
existing_node
.attrs
.attrs
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
let marks_vec: Vec<Mark> =
existing_node.marks.iter().cloned().collect();
let child_type = self
.schema
.nodes
.get(&type_name)
.ok_or_else(|| {
schema_error(&format!(
"无法在 schema 中找到节点类型:{type_name}"
))
})?;
let child_tree = self.create_tree_with_type(
child_type,
Some(existing_node.id.clone()),
Some(&attrs_map),
vec![],
Some(marks_vec),
)?;
let child_id = child_tree.0.id.clone();
final_content_ids.push(child_id);
filled_nodes.push(child_tree);
} else {
let child_type = self
.schema
.nodes
.get(&type_name)
.ok_or_else(|| {
schema_error(&format!(
"无法在 schema 中找到节点类型:{type_name}"
))
})?;
let child_tree = self.create_tree_with_type(
child_type,
None,
None,
vec![],
None,
)?;
let child_id = child_tree.0.id.clone();
final_content_ids.push(child_id);
filled_nodes.push(child_tree);
}
}
}
}
}
let node = Node::new(
&id,
node_type.name.clone(),
computed_attrs,
final_content_ids,
computed_marks,
);
Ok(NodeTree(node, filled_nodes))
}
fn instantiate_node(
node_type: &NodeDefinition,
id: Option<NodeId>,
attrs: Option<&HashMap<String, Value>>,
content: Vec<NodeId>,
marks: Option<Vec<Mark>>,
) -> Node {
let id: NodeId = id.unwrap_or_else(IdGenerator::get_id);
let attrs = node_type.compute_attrs(attrs);
let marks = node_type.compute_marks(marks);
Node::new(&id, node_type.name.clone(), attrs, content, marks)
}
pub(crate) fn instantiate_mark(
mark_def: &MarkDefinition,
attrs: Option<&HashMap<String, Value>>,
) -> Mark {
Mark {
r#type: mark_def.name.clone(),
attrs: mark_def.compute_attrs(attrs),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mark_definition::MarkSpec;
use crate::node_definition::NodeSpec;
use crate::schema::{Schema, SchemaSpec};
fn build_schema() -> Schema {
let mut spec = SchemaSpec {
nodes: HashMap::new(),
marks: HashMap::new(),
top_node: Some("doc".to_string()),
};
spec.nodes.insert("doc".to_string(), NodeSpec::default());
spec.nodes.insert("paragraph".to_string(), NodeSpec::default());
spec.marks.insert("bold".to_string(), MarkSpec::default());
Schema::compile(spec).expect("schema should compile")
}
#[test]
fn ensure_node_returns_descriptive_error() {
let schema = build_schema();
let factory = NodeFactory::new(&schema);
let err = factory.ensure_node("unknown").unwrap_err();
let msg = err.to_string();
assert!(msg.contains("未找到节点类型"), "actual: {msg}");
assert!(msg.contains("unknown"), "actual: {msg}");
assert!(msg.contains("doc"), "actual: {msg}");
}
#[test]
fn ensure_mark_returns_descriptive_error() {
let schema = build_schema();
let factory = NodeFactory::new(&schema);
let err = factory.ensure_mark("italic").unwrap_err();
let msg = err.to_string();
assert!(msg.contains("未找到标记类型"), "actual: {msg}");
assert!(msg.contains("italic"), "actual: {msg}");
assert!(msg.contains("bold"), "actual: {msg}");
}
#[test]
fn node_and_mark_names_exposed() {
let schema = build_schema();
let factory = NodeFactory::new(&schema);
let nodes = factory.node_names();
assert!(nodes.contains(&"doc"));
let marks = factory.mark_names();
assert!(marks.contains(&"bold"));
}
}