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 serde_json::Value;
8use std::any::Any;
9use std::collections::HashMap;
10use std::error::Error;
11use std::sync::{Arc, Mutex};
12#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize)]
15pub struct Attribute {
16 pub has_default: bool,
17 pub default: Option<Value>,
18}
19
20impl Attribute {
21 pub(crate) fn new(options: AttributeSpec) -> Self {
23 Attribute {
24 has_default: options.default.is_some(),
25 default: options.default,
26 }
27 }
28 pub fn is_required(&self) -> bool {
31 !self.has_default
32 }
33}
34#[derive(Clone, Debug)]
37pub struct Schema {
38 pub spec: SchemaSpec,
40 pub top_node_type: Option<NodeType>,
42 pub cached: Arc<Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>>,
44 pub nodes: HashMap<String, NodeType>,
46 pub marks: HashMap<String, MarkType>,
48}
49impl PartialEq for Schema {
50 fn eq(
51 &self,
52 other: &Self,
53 ) -> bool {
54 self.spec == other.spec
55 && self.top_node_type == other.top_node_type
56 && self.nodes == other.nodes
57 && self.marks == other.marks
58 }
59}
60impl Eq for Schema {}
61impl Schema {
62 pub fn new(spec: SchemaSpec) -> Self {
64 let mut instance_spec = SchemaSpec {
65 nodes: HashMap::new(),
66 marks: HashMap::new(),
67 top_node: spec.top_node,
68 };
69 for (key, value) in spec.nodes {
71 instance_spec.nodes.insert(key, value);
72 }
73 for (key, value) in spec.marks {
74 instance_spec.marks.insert(key, value);
75 }
76 Schema {
77 spec: instance_spec,
78 top_node_type: None,
79 cached: Arc::new(Mutex::new(HashMap::new())),
80 nodes: HashMap::new(),
81 marks: HashMap::new(),
82 }
83 }
84 pub fn compile(
87 instance_spec: SchemaSpec
88 ) -> Result<Schema, Box<dyn Error>> {
89 let mut schema: Schema = Schema::new(instance_spec);
90 let nodes: HashMap<String, NodeType> =
91 NodeType::compile(schema.spec.nodes.clone());
92 let marks = MarkType::compile(schema.spec.marks.clone());
93 let mut content_expr_cache = HashMap::new();
94 let mut updated_nodes = HashMap::new();
95 for (prop, type_) in &nodes {
96 if marks.contains_key(prop) {
97 return Err(format!("{} 不能既是节点又是标记", prop).into());
98 }
99
100 let content_expr = type_.spec.content.as_deref().unwrap_or("");
101 let mark_expr = type_.spec.marks.as_deref();
102
103 let content_match = content_expr_cache
104 .entry(content_expr.to_string())
105 .or_insert_with(|| {
106 ContentMatch::parse(content_expr.to_string(), &nodes)
107 })
108 .clone();
109
110 let mark_set = match mark_expr {
111 Some("_") => None,
112 Some(expr) => {
113 let marks_result = gather_marks(
114 &schema,
115 expr.split_whitespace().collect(),
116 );
117 match marks_result {
118 Ok(marks) => Some(marks.into_iter().cloned().collect()), Err(e) => return Err(e.into()),
120 }
121 },
122 None => None,
123 };
124
125 let mut node = type_.clone();
126 node.content_match = Some(content_match);
127 node.mark_set = mark_set;
128 updated_nodes.insert(prop.clone(), node);
129 }
130 schema.nodes = updated_nodes;
131 schema.marks = marks;
132 schema.top_node_type = schema
133 .nodes
134 .get(
135 &schema
136 .spec
137 .top_node
138 .clone()
139 .unwrap_or_else(|| "doc".to_string()),
140 )
141 .cloned();
142
143 Ok(schema)
144 }
145}
146#[derive(Clone, PartialEq, Eq, Debug)]
149pub struct SchemaSpec {
150 pub nodes: HashMap<String, NodeSpec>,
151 pub marks: HashMap<String, MarkSpec>,
152 pub top_node: Option<String>,
153}
154
155pub fn default_attrs(
160 attrs: &HashMap<String, Attribute>
161) -> Option<HashMap<String, Value>> {
162 let mut defaults = HashMap::new();
163
164 for (attr_name, attr) in attrs {
165 if let Some(default) = &attr.default {
166 defaults.insert(attr_name.clone(), default.clone());
167 } else {
168 return None;
169 }
170 }
171
172 Some(defaults)
173}
174#[derive(Clone, PartialEq, Debug, Eq, Hash, Serialize)]
176pub struct AttributeSpec {
177 pub default: Option<Value>,
179}
180fn gather_marks<'a>(
183 schema: &'a Schema,
184 marks: Vec<&'a str>,
185) -> Result<Vec<&'a MarkType>, String> {
186 let mut found = Vec::new();
187
188 for name in marks {
189 if let Some(mark) = schema.marks.get(name) {
190 found.push(mark);
191 } else {
192 let mut ok = None;
193 for mark_ref in schema.marks.values() {
194 if name == "_"
195 || mark_ref.spec.group.as_ref().is_some_and(|group| {
196 group.split_whitespace().any(|g| g == name)
197 })
198 {
199 found.push(mark_ref);
200 ok = Some(mark_ref);
201 break;
202 }
203 }
204 if ok.is_none() {
205 return Err(format!("未知的标记类型: '{}'", name));
206 }
207 }
208 }
209 Ok(found)
210}
211pub fn compute_attrs(
214 attrs: &HashMap<String, Attribute>,
215 value: Option<&HashMap<String, Value>>,
216) -> Attrs {
217 let mut built = ImHashMap::new();
218
219 for (name, attr) in attrs {
220 let given = value.and_then(|v| v.get(name));
221
222 let given = match given {
223 Some(val) => val.clone(),
224 None => {
225 if attr.has_default {
226 attr.default.clone().unwrap_or_else(|| {
227 panic!("没有为属性提供默认值 {}", name)
228 })
229 } else {
230 Value::Null
231 }
232 },
233 };
234
235 built.insert(name.clone(), given);
236 }
237
238 built
239}