1use dmc_diagnostic::Code;
6use duck_diagnostic::DiagnosticEngine;
7use serde_json::Value;
8use std::path::Path;
9
10use crate::engine::compile::Compiler;
11
12pub struct Loaded {
15 pub data: Value,
16 pub content: String,
17}
18
19pub trait Loader: Send + Sync {
21 fn test(&self, path: &Path) -> bool;
22 fn load(&self, path: &Path, source: &str, diag_engine: &mut DiagnosticEngine<Code>) -> Result<Loaded, String>;
23}
24
25pub struct MatterLoader;
29
30impl Loader for MatterLoader {
31 fn test(&self, path: &Path) -> bool {
32 matches!(path.extension().and_then(|s| s.to_str()), Some("md") | Some("mdx") | Some("markdown"))
33 }
34
35 fn load(&self, _path: &Path, source: &str, diag_engine: &mut DiagnosticEngine<Code>) -> Result<Loaded, String> {
36 let out = Compiler::compile(source, diag_engine);
37 let mut data = if let Value::Object(_) = out.frontmatter {
38 out.frontmatter.clone()
39 } else {
40 Value::Object(serde_json::Map::new())
41 };
42 if let Value::Object(map) = &mut data {
43 map.insert("__compiled".into(), serde_json::to_value(&out).unwrap_or(Value::Null));
44 }
45 Ok(Loaded { data, content: source.to_string() })
46 }
47}
48
49pub struct YamlLoader;
52
53impl Loader for YamlLoader {
54 fn test(&self, path: &Path) -> bool {
55 matches!(path.extension().and_then(|s| s.to_str()), Some("yaml") | Some("yml"))
56 }
57
58 fn load(&self, _path: &Path, source: &str, _diag_engine: &mut DiagnosticEngine<Code>) -> Result<Loaded, String> {
59 let v: serde_yaml::Value = serde_yaml::from_str(source).map_err(|e| format!("yaml parse: {e}"))?;
60 let json = serde_json::to_value(v).map_err(|e| format!("yaml->json: {e}"))?;
61 Ok(Loaded { data: json, content: source.to_string() })
62 }
63}
64
65pub struct JsonLoader;
67
68impl Loader for JsonLoader {
69 fn test(&self, path: &Path) -> bool {
70 matches!(path.extension().and_then(|s| s.to_str()), Some("json"))
71 }
72
73 fn load(&self, _path: &Path, source: &str, _diag_engine: &mut DiagnosticEngine<Code>) -> Result<Loaded, String> {
74 let v: Value = serde_json::from_str(source).map_err(|e| format!("json parse: {e}"))?;
75 Ok(Loaded { data: v, content: source.to_string() })
76 }
77}
78
79pub struct LoaderRegistry {
81 loaders: Vec<Box<dyn Loader>>,
82}
83
84impl LoaderRegistry {
85 pub fn with_defaults() -> Self {
87 Self { loaders: vec![Box::new(MatterLoader), Box::new(YamlLoader), Box::new(JsonLoader)] }
88 }
89
90 pub fn pick(&self, path: &Path) -> Option<&dyn Loader> {
92 self.loaders.iter().find(|l| l.test(path)).map(|l| l.as_ref())
93 }
94}