moduforge_core/model/
schema.rs1use super::attrs::Attrs;
2use super::content::ContentMatch;
3use super::mark_type::{MarkSpec, MarkType};
4use super::node_type::{NodeSpec, NodeType};
5use im::HashMap as ImHashMap;
6use serde::Serialize;
7use std::any::Any;
8use std::collections::HashMap;
9use std::error::Error;
10use std::sync::{Arc, Mutex};
11#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize)]
14pub struct Attribute {
15 pub has_default: bool,
16 pub default: Option<String>,
17}
18
19impl Attribute {
20 pub(crate) fn new(options: AttributeSpec) -> Self {
22 Attribute { has_default: options.default.is_some(), default: options.default }
23 }
24 pub fn is_required(&self) -> bool {
27 !self.has_default
28 }
29}
30#[derive(Clone, Debug)]
33pub struct Schema {
34 pub spec: SchemaSpec,
36 pub top_node_type: Option<NodeType>,
38 pub cached: Arc<Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>>,
40 pub nodes: HashMap<String, NodeType>,
42 pub marks: HashMap<String, MarkType>,
44}
45impl PartialEq for Schema {
46 fn eq(
47 &self,
48 other: &Self,
49 ) -> bool {
50 self.spec == other.spec
51 && self.top_node_type == other.top_node_type
52 && self.nodes == other.nodes
53 && self.marks == other.marks
54 }
55}
56impl Eq for Schema {}
57impl Schema {
58 pub fn new(spec: SchemaSpec) -> Self {
60 let mut instance_spec = SchemaSpec { nodes: HashMap::new(), marks: HashMap::new(), top_node: spec.top_node };
61 for (key, value) in spec.nodes {
63 instance_spec.nodes.insert(key, value);
64 }
65 for (key, value) in spec.marks {
66 instance_spec.marks.insert(key, value);
67 }
68 Schema {
69 spec: instance_spec,
70 top_node_type: None,
71 cached: Arc::new(Mutex::new(HashMap::new())),
72 nodes: HashMap::new(),
73 marks: HashMap::new(),
74 }
75 }
76 pub fn compile(instance_spec: SchemaSpec) -> Result<Schema, Box<dyn Error>> {
79 let mut schema: Schema = Schema::new(instance_spec);
80 let nodes: HashMap<String, NodeType> = NodeType::compile(schema.spec.nodes.clone());
81 let marks = MarkType::compile(schema.spec.marks.clone());
82 let mut content_expr_cache = HashMap::new();
83 let mut updated_nodes = HashMap::new();
84 for (prop, type_) in &nodes {
85 if marks.contains_key(prop) {
86 return Err(format!("{} 不能既是节点又是标记", prop).into());
87 }
88
89 let content_expr = type_.spec.content.as_deref().unwrap_or("");
90 let mark_expr = type_.spec.marks.as_deref();
91
92 let content_match = content_expr_cache
93 .entry(content_expr.to_string())
94 .or_insert_with(|| ContentMatch::parse(content_expr.to_string(), &nodes))
95 .clone();
96
97 let mark_set = match mark_expr {
98 Some("_") => None,
99 Some(expr) => {
100 let marks_result = gather_marks(&schema, expr.split_whitespace().collect());
101 match marks_result {
102 Ok(marks) => Some(marks.into_iter().cloned().collect()), Err(e) => return Err(e.into()),
104 }
105 },
106 None => None,
107 };
108
109 let mut node = type_.clone();
110 node.content_match = Some(content_match);
111 node.mark_set = mark_set;
112 updated_nodes.insert(prop.clone(), node);
113 }
114 schema.nodes = updated_nodes;
115 schema.marks = marks;
116 schema.top_node_type =
117 schema.nodes.get(&schema.spec.top_node.clone().unwrap_or_else(|| "doc".to_string())).cloned();
118
119 Ok(schema)
120 }
121}
122#[derive(Clone, PartialEq, Eq, Debug)]
125pub struct SchemaSpec {
126 pub nodes: HashMap<String, NodeSpec>,
127 pub marks: HashMap<String, MarkSpec>,
128 pub top_node: Option<String>,
129}
130
131pub fn default_attrs(attrs: &HashMap<String, Attribute>) -> Option<HashMap<String, String>> {
136 let mut defaults = HashMap::new();
137
138 for (attr_name, attr) in attrs {
139 if let Some(default) = &attr.default {
140 defaults.insert(attr_name.clone(), default.clone());
141 } else {
142 return None;
143 }
144 }
145
146 Some(defaults)
147}
148#[derive(Clone, PartialEq, Debug, Eq, Hash, Serialize)]
150pub struct AttributeSpec {
151 pub default: Option<String>,
153}
154fn gather_marks<'a>(
157 schema: &'a Schema,
158 marks: Vec<&'a str>,
159) -> Result<Vec<&'a MarkType>, String> {
160 let mut found = Vec::new();
161
162 for name in marks {
163 if let Some(mark) = schema.marks.get(name) {
164 found.push(mark);
165 } else {
166 let mut ok = None;
167 for mark_ref in schema.marks.values() {
168 if name == "_"
169 || mark_ref.spec.group.as_ref().is_some_and(|group| group.split_whitespace().any(|g| g == name))
170 {
171 found.push(mark_ref);
172 ok = Some(mark_ref);
173 break;
174 }
175 }
176 if ok.is_none() {
177 return Err(format!("未知的标记类型: '{}'", name));
178 }
179 }
180 }
181 Ok(found)
182}
183pub fn compute_attrs(
186 attrs: &HashMap<String, Attribute>,
187 value: Option<&HashMap<String, String>>,
188) -> Attrs {
189 let mut built = ImHashMap::new();
190
191 for (name, attr) in attrs {
192 let given = value.and_then(|v| v.get(name));
193
194 let given = match given {
195 Some(val) => val.clone(),
196 None => {
197 if attr.has_default {
198 attr.default.clone().unwrap_or_else(|| panic!("没有为属性提供默认值 {}", name))
199 } else {
200 "".to_string()
201 }
202 },
203 };
204
205 built.insert(name.clone(), given);
206 }
207
208 built
209}