Add host-structured imports
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
use decodal::{
|
||||
Data, Diagnostic, DiagnosticKind, Engine, HostValue, ImportLoader, LoadedImport, Span,
|
||||
};
|
||||
|
||||
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<HostValue> {
|
||||
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" => HostValue::bool(true),
|
||||
"false" => HostValue::bool(false),
|
||||
value => HostValue::string(value),
|
||||
};
|
||||
fields.push((name.trim(), value));
|
||||
}
|
||||
Ok(HostValue::object([
|
||||
("frontmatter", HostValue::object(fields)),
|
||||
("body", HostValue::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(())
|
||||
}
|
||||
Reference in New Issue
Block a user