mf_core/extension_manager/
mod.rs1use std::sync::Arc;
2use std::time::Instant;
3
4use mf_model::schema::Schema;
5use mf_state::plugin::Plugin;
6
7use crate::{
8 helpers::get_schema_by_resolved_extensions::get_schema_by_resolved_extensions,
9 metrics, types::Extensions, ForgeResult, XmlSchemaParser, extension::OpFn,
10};
11pub struct ExtensionManager {
13 plugins: Vec<Arc<Plugin>>,
14 schema: Arc<Schema>,
15 op_fns: OpFn,
16}
17#[derive(Default)]
21pub struct ExtensionManagerBuilder {
22 extensions: Vec<Extensions>,
23 xml_files: Vec<String>,
24 xml_contents: Vec<String>,
25}
26
27impl ExtensionManagerBuilder {
28 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn add_extension(
49 mut self,
50 extension: Extensions,
51 ) -> Self {
52 self.extensions.push(extension);
53 self
54 }
55
56 pub fn add_extensions(
61 mut self,
62 extensions: Vec<Extensions>,
63 ) -> Self {
64 self.extensions.extend(extensions);
65 self
66 }
67
68 pub fn add_xml_file<P: AsRef<str>>(
83 mut self,
84 xml_file_path: P,
85 ) -> Self {
86 self.xml_files.push(xml_file_path.as_ref().to_string());
87 self
88 }
89
90 pub fn add_xml_files<P: AsRef<str>>(
95 mut self,
96 xml_file_paths: &[P],
97 ) -> Self {
98 for path in xml_file_paths {
99 self.xml_files.push(path.as_ref().to_string());
100 }
101 self
102 }
103
104 pub fn add_xml_content<S: AsRef<str>>(
129 mut self,
130 xml_content: S,
131 ) -> Self {
132 self.xml_contents.push(xml_content.as_ref().to_string());
133 self
134 }
135
136 pub fn add_xml_contents<S: AsRef<str>>(
141 mut self,
142 xml_contents: &[S],
143 ) -> Self {
144 for content in xml_contents {
145 self.xml_contents.push(content.as_ref().to_string());
146 }
147 self
148 }
149
150 pub fn build(self) -> ForgeResult<ExtensionManager> {
155 let start_time = Instant::now();
156 let mut all_extensions = self.extensions;
157
158 for xml_file in &self.xml_files {
160 let extensions =
161 XmlSchemaParser::parse_multi_file_to_extensions(xml_file)
162 .map_err(|e| {
163 crate::error::error_utils::config_error(format!(
164 "解析XML文件 {xml_file} 失败: {e}"
165 ))
166 })?;
167 all_extensions.extend(extensions);
168 }
169
170 for xml_content in &self.xml_contents {
172 let extensions = XmlSchemaParser::parse_to_extensions(xml_content)
173 .map_err(|e| {
174 crate::error::error_utils::config_error(format!(
175 "解析XML内容失败: {e}"
176 ))
177 })?;
178 all_extensions.extend(extensions);
179 }
180
181 metrics::xml_parsing_duration(start_time.elapsed());
182
183 ExtensionManager::new(&all_extensions)
185 }
186}
187
188impl ExtensionManager {
189 pub fn builder() -> ExtensionManagerBuilder {
200 ExtensionManagerBuilder::new()
201 }
202
203 pub fn new(extensions: &Vec<Extensions>) -> ForgeResult<Self> {
204 let start_time = Instant::now();
205 let schema = Arc::new(get_schema_by_resolved_extensions(extensions)?);
206 let mut plugins = vec![];
207 let mut op_fns = vec![];
208 let mut extension_count = 0;
209 let mut plugin_count = 0;
210 for extension in extensions {
211 if let Extensions::E(extension) = extension {
212 extension_count += 1;
213 for plugin in extension.get_plugins() {
214 plugin_count += 1;
215 plugins.push(plugin.clone());
216 }
217 for op_fn in extension.get_op_fns() {
218 op_fns.push(op_fn.clone());
219 }
220 }
221 }
222
223 metrics::extensions_loaded(extension_count);
224 metrics::plugins_loaded(plugin_count);
225 metrics::extension_manager_creation_duration(start_time.elapsed());
226
227 Ok(ExtensionManager { schema, plugins, op_fns })
228 }
229
230 pub fn from_xml_file(xml_file_path: &str) -> ForgeResult<Self> {
245 Self::builder().add_xml_file(xml_file_path).build()
246 }
247
248 pub fn from_xml_string(xml_content: &str) -> ForgeResult<Self> {
274 Self::builder().add_xml_content(xml_content).build()
275 }
276
277 pub fn from_xml_files(xml_file_paths: &[&str]) -> ForgeResult<Self> {
298 Self::builder().add_xml_files(xml_file_paths).build()
299 }
300
301 pub fn from_mixed_sources(
323 extensions: &[Extensions],
324 xml_file_paths: &[&str],
325 ) -> ForgeResult<Self> {
326 Self::builder()
327 .add_extensions(extensions.to_vec())
328 .add_xml_files(xml_file_paths)
329 .build()
330 }
331 pub fn get_op_fns(&self) -> &OpFn {
332 &self.op_fns
333 }
334
335 pub fn get_schema(&self) -> Arc<Schema> {
336 self.schema.clone()
337 }
338 pub fn get_plugins(&self) -> &Vec<Arc<Plugin>> {
339 &self.plugins
340 }
341
342 pub fn add_restored_plugins(
344 &mut self,
345 plugins: Vec<std::sync::Arc<mf_state::plugin::Plugin>>,
346 ) -> ForgeResult<()> {
347 self.plugins.extend(plugins);
348 Ok(())
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_extension_manager_from_xml_string() {
358 let xml = r#"
359 <?xml version="1.0" encoding="UTF-8"?>
360 <schema top_node="doc">
361 <nodes>
362 <node name="doc" group="block">
363 <desc>文档根节点</desc>
364 <content>paragraph+</content>
365 </node>
366 <node name="paragraph" group="block">
367 <desc>段落节点</desc>
368 <content>text*</content>
369 <marks>strong</marks>
370 </node>
371 <node name="text">
372 <desc>文本节点</desc>
373 </node>
374 </nodes>
375 <marks>
376 <mark name="strong" group="formatting">
377 <desc>粗体标记</desc>
378 <spanning>true</spanning>
379 </mark>
380 </marks>
381 </schema>
382 "#;
383
384 let result = ExtensionManager::from_xml_string(xml);
385 assert!(result.is_ok());
386
387 let manager = result.unwrap();
388 let schema = manager.get_schema();
389
390 assert_eq!(schema.nodes.len(), 3);
392 assert_eq!(schema.marks.len(), 1);
393 assert!(schema.nodes.contains_key("doc"));
394 assert!(schema.nodes.contains_key("paragraph"));
395 assert!(schema.nodes.contains_key("text"));
396 assert!(schema.marks.contains_key("strong"));
397 }
398
399 #[test]
400 fn test_extension_manager_from_xml_files() {
401 let temp_dir = std::env::temp_dir().join("extension_manager_test");
403 std::fs::create_dir_all(&temp_dir).unwrap();
404
405 let base_nodes_content = r#"
407 <?xml version="1.0" encoding="UTF-8"?>
408 <schema>
409 <nodes>
410 <node name="doc" group="block">
411 <desc>文档根节点</desc>
412 <content>paragraph+</content>
413 </node>
414 <node name="paragraph" group="block">
415 <desc>段落节点</desc>
416 <content>text*</content>
417 </node>
418 <node name="text">
419 <desc>文本节点</desc>
420 </node>
421 </nodes>
422 </schema>
423 "#;
424
425 let base_nodes_path = temp_dir.join("base-nodes.xml");
426 std::fs::write(&base_nodes_path, base_nodes_content).unwrap();
427
428 let marks_content = r#"
430 <?xml version="1.0" encoding="UTF-8"?>
431 <schema>
432 <marks>
433 <mark name="strong" group="formatting">
434 <desc>粗体标记</desc>
435 <spanning>true</spanning>
436 </mark>
437 <mark name="em" group="formatting">
438 <desc>斜体标记</desc>
439 <spanning>true</spanning>
440 </mark>
441 </marks>
442 </schema>
443 "#;
444
445 let marks_path = temp_dir.join("marks.xml");
446 std::fs::write(&marks_path, marks_content).unwrap();
447
448 let files = vec![
450 base_nodes_path.to_str().unwrap(),
451 marks_path.to_str().unwrap(),
452 ];
453
454 let result = ExtensionManager::from_xml_files(&files);
455 assert!(result.is_ok());
456
457 let manager = result.unwrap();
458 let schema = manager.get_schema();
459
460 assert_eq!(schema.nodes.len(), 3); assert_eq!(schema.marks.len(), 2); std::fs::remove_dir_all(&temp_dir).unwrap();
466 }
467
468 #[test]
469 fn test_extension_manager_builder_mixed_sources() {
470 use crate::node::Node;
471 use crate::mark::Mark;
472 use mf_model::node_definition::NodeSpec;
473 use mf_model::mark_definition::MarkSpec;
474
475 let code_node = Node::create("code_node", NodeSpec::default());
477 let code_mark = Mark::new("code_mark", MarkSpec::default());
478
479 let xml_content = r#"
480 <?xml version="1.0" encoding="UTF-8"?>
481 <schema>
482 <nodes>
483 <node name="xml_node" group="block">
484 <desc>XML定义的节点</desc>
485 </node>
486 </nodes>
487 <marks>
488 <mark name="xml_mark" group="formatting">
489 <desc>XML定义的标记</desc>
490 </mark>
491 </marks>
492 </schema>
493 "#;
494
495 let result = ExtensionManager::builder()
497 .add_extension(Extensions::N(code_node))
498 .add_extension(Extensions::M(code_mark))
499 .add_xml_content(xml_content)
500 .build();
501
502 if let Err(ref e) = result {
503 println!("Error: {e:?}");
504 }
505 assert!(result.is_ok());
506
507 let manager = result.unwrap();
508 let schema = manager.get_schema();
509
510 assert_eq!(schema.nodes.len(), 2); assert_eq!(schema.marks.len(), 2); assert!(schema.nodes.contains_key("code_node"));
515 assert!(schema.nodes.contains_key("xml_node"));
516 assert!(schema.marks.contains_key("code_mark"));
517 assert!(schema.marks.contains_key("xml_mark"));
518 }
519}