use markplus_core::json::SiteAsset;
use crate::context::ast_to_template_context;
use crate::engine::RenderEngine;
use crate::error::RenderError;
impl RenderEngine {
pub fn render_html(
&self,
asset: &SiteAsset,
template_name: &str,
) -> Result<String, RenderError> {
let ctx_val = ast_to_template_context(asset);
let ctx = tera::Context::from_value(ctx_val)?;
self.tera.render(template_name, &ctx).map_err(RenderError::from)
}
pub fn render_typst_string(
&self,
asset: &SiteAsset,
template_name: &str,
) -> Result<String, RenderError> {
let ctx_val = ast_to_template_context(asset);
let ctx = tera::Context::from_value(ctx_val)?;
self.tera.render(template_name, &ctx).map_err(RenderError::from)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn compile_pdf(&self, typst_src: &str) -> Result<Vec<u8>, RenderError> {
use typst::diag::Warned;
use typst::layout::PagedDocument;
use typst_as_lib::TypstEngine;
use typst_as_lib::typst_kit_options::TypstKitFontOptions;
let engine = TypstEngine::builder()
.with_static_source_file_resolver([("main.typ", typst_src.to_string())])
.with_package_file_resolver()
.search_fonts_with(TypstKitFontOptions::default())
.build();
let warned: Warned<Result<PagedDocument, _>> = engine.compile("main.typ");
for warning in &warned.warnings {
eprintln!("typst warning: {}", warning.message);
}
let document: PagedDocument = warned
.output
.map_err(|e| RenderError::TypstCompile(format!("{:?}", e)))?;
if document.pages.is_empty() {
return Err(RenderError::TypstCompile("document has no pages".into()));
}
let options = typst_pdf::PdfOptions::default();
typst_pdf::pdf(&document, &options)
.map_err(|e| RenderError::TypstCompile(format!("{:?}", e)))
}
#[cfg(target_arch = "wasm32")]
pub fn compile_pdf(&self, typst_src: &str) -> Result<Vec<u8>, RenderError> {
use crate::wasm_world::WasmWorld;
use typst::layout::PagedDocument;
let world = WasmWorld::new(typst_src.to_string());
let warned = typst::compile(&world);
let document: PagedDocument = warned.output.map_err(|errors| {
let msg = errors
.into_iter()
.map(|e| format!("{:?}", e))
.collect::<Vec<_>>()
.join("\n");
RenderError::TypstCompile(msg)
})?;
if document.pages.is_empty() {
return Err(RenderError::TypstCompile("document has no pages".into()));
}
let options = typst_pdf::PdfOptions::default();
typst_pdf::pdf(&document, &options)
.map_err(|e| RenderError::TypstCompile(format!("{:?}", e)))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn render_to_file(
&self,
asset: &SiteAsset,
template_name: &str,
dest: &std::path::Path,
) -> Result<(), RenderError> {
use std::fs;
let ext = dest.extension().and_then(|e| e.to_str()).unwrap_or("");
match ext {
"html" => {
let html = self.render_html(asset, template_name)?;
fs::write(dest, html).map_err(RenderError::from)?;
}
"typ" => {
let typ_src = self.render_typst_string(asset, template_name)?;
fs::write(dest, typ_src).map_err(RenderError::from)?;
}
"pdf" => {
let typ_src = self.render_typst_string(asset, template_name)?;
let pdf_bytes = self.compile_pdf(&typ_src)?;
fs::write(dest, pdf_bytes).map_err(RenderError::from)?;
}
other => {
return Err(RenderError::Io(format!(
"unsupported output extension: {:?} (use .html, .typ, or .pdf)",
other
)));
}
}
Ok(())
}
}