86 lines
2.4 KiB
Rust
86 lines
2.4 KiB
Rust
use decodal::{Data, Diagnostic, DiagnosticKind, Engine, ImportLoader, LoadedImport, Span, Value};
|
|
|
|
const POST: &str = r#"---
|
|
title: Hello
|
|
draft: false
|
|
---
|
|
# Hello
|
|
|
|
This body stays as Markdown.
|
|
"#;
|
|
|
|
struct ContentLoader;
|
|
|
|
impl ImportLoader for ContentLoader {
|
|
fn load(
|
|
&mut self,
|
|
_current_key: Option<&str>,
|
|
specifier: &str,
|
|
) -> decodal::Result<LoadedImport> {
|
|
if specifier != "./post.md" {
|
|
return Err(Diagnostic::new(
|
|
DiagnosticKind::Import,
|
|
Span::default(),
|
|
format!("unknown content import `{specifier}`"),
|
|
));
|
|
}
|
|
Ok(LoadedImport::value(
|
|
"content/post.md",
|
|
parse_markdown(POST)?,
|
|
))
|
|
}
|
|
}
|
|
|
|
fn parse_markdown(source: &str) -> decodal::Result<Value> {
|
|
let source = source.strip_prefix("---\n").ok_or_else(frontmatter_error)?;
|
|
let (frontmatter, body) = source.split_once("\n---\n").ok_or_else(frontmatter_error)?;
|
|
let mut fields = Vec::new();
|
|
for line in frontmatter.lines() {
|
|
let (name, value) = line.split_once(':').ok_or_else(frontmatter_error)?;
|
|
let value = match value.trim() {
|
|
"true" => Value::bool(true),
|
|
"false" => Value::bool(false),
|
|
value => Value::string(value),
|
|
};
|
|
fields.push((name.trim(), value));
|
|
}
|
|
Ok(Value::object([
|
|
("frontmatter", Value::object(fields)),
|
|
("body", Value::string(body)),
|
|
]))
|
|
}
|
|
|
|
fn frontmatter_error() -> Diagnostic {
|
|
Diagnostic::new(
|
|
DiagnosticKind::Import,
|
|
Span::default(),
|
|
"invalid Markdown frontmatter",
|
|
)
|
|
}
|
|
|
|
fn main() -> decodal::Result<()> {
|
|
let mut engine = Engine::new(ContentLoader);
|
|
let module = engine.add_root_source(
|
|
"main.dcdl",
|
|
"main.dcdl",
|
|
r#"
|
|
let post = import "./post.md";
|
|
in {
|
|
title = post.frontmatter.title;
|
|
draft = post.frontmatter.draft;
|
|
body = post.body;
|
|
}
|
|
"#,
|
|
)?;
|
|
let value = engine.eval_module(module)?;
|
|
let data = engine.materialize(&value)?;
|
|
|
|
let Data::Object(fields) = data else {
|
|
panic!("expected imported Markdown to produce an object")
|
|
};
|
|
assert_eq!(fields[0].value, Data::String(String::from("Hello")));
|
|
assert_eq!(fields[1].value, Data::Bool(false));
|
|
assert!(matches!(fields[2].value, Data::String(_)));
|
|
Ok(())
|
|
}
|