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