use anyhow::{anyhow, Result};
use easy_error::Error as BundError;
use rust_multistackvm::multistackvm::VM;
use super::helpers::{
active_config, active_store, pull, require_depth, resolve_fs_path, value_to_string,
};
use crate::manuscript::{ManuscriptChapter, ManuscriptMeta};
use crate::project::ProjectLayout;
use crate::store::hierarchy::Hierarchy;
use crate::store::node::Node;
type BundResult<'a> = std::result::Result<&'a mut VM, BundError>;
fn to_bund_err(e: anyhow::Error) -> BundError {
easy_error::err_msg(e.to_string())
}
pub fn register(vm: &mut VM) -> Result<()> {
let words: &[(&str, fn(&mut VM) -> BundResult<'_>)] = &[
("ink.export.docx", w_docx),
("ink.export.manuscript", w_manuscript),
("ink.export.markdown", w_markdown),
("ink.export.tex", w_tex),
("ink.export.epub", w_epub),
];
for (name, f) in words {
vm.register_inline(name.to_string(), *f)
.map_err(|e| anyhow!("register {name}: {e}"))?;
}
Ok(())
}
struct ExportCtx {
path: std::path::PathBuf,
layout: ProjectLayout,
cfg: &'static crate::config::Config,
h: Hierarchy,
book: Node,
}
fn prologue(vm: &mut VM, tag: &str) -> Result<ExportCtx> {
require_depth(vm, 2, tag)?;
let path = value_to_string(pull(vm, tag)?, "path", tag)?;
let book = value_to_string(pull(vm, tag)?, "book", tag)?;
let resolved = resolve_fs_path(tag, &path)?;
let store = active_store(tag)?;
let cfg = active_config(tag)?;
let layout = ProjectLayout::new(store.project_root());
let h = Hierarchy::load(store).map_err(|e| anyhow!("{tag}: hierarchy: {e}"))?;
let bn = if book.trim().is_empty() {
None
} else {
Some(book.as_str())
};
let node = crate::cli::resolve_user_book(&h, bn, tag)
.map_err(|e| anyhow!("{tag}: {e}"))?
.clone();
Ok(ExportCtx {
path: resolved,
layout,
cfg,
h,
book: node,
})
}
fn model(ctx: &ExportCtx) -> Result<(ManuscriptMeta, Vec<ManuscriptChapter>)> {
crate::cli::manuscript::build_model(&ctx.layout, ctx.cfg, &ctx.h, &ctx.book, None, None, None)
.map_err(|e| anyhow!("{e}"))
}
fn combined(ctx: &ExportCtx, tag: &str) -> Result<String> {
crate::export::assemble_typst_source_filtered(&ctx.layout, &ctx.h, Some(ctx.book.id), None, None)
.map_err(|e| anyhow!("{tag}: assemble: {e}"))
}
fn write_bytes(ctx: &ExportCtx, tag: &str, bytes: &[u8]) -> Result<()> {
crate::io_atomic::write(&ctx.path, bytes)
.map_err(|e| anyhow!("{tag}: write {}: {e}", ctx.path.display()))
}
fn w_docx(vm: &mut VM) -> BundResult<'_> {
do_docx(vm).map_err(to_bund_err)
}
fn do_docx(vm: &mut VM) -> Result<&mut VM> {
let tag = "ink.export.docx";
let ctx = prologue(vm, tag)?;
let (meta, chapters) = model(&ctx)?;
let bytes = crate::export::docx::build_docx(
&meta,
&chapters,
crate::export::docx::DocxFont::TimesNewRoman,
)
.map_err(|e| anyhow!("{tag}: {e}"))?;
write_bytes(&ctx, tag, &bytes)?;
Ok(vm)
}
fn w_manuscript(vm: &mut VM) -> BundResult<'_> {
do_manuscript(vm).map_err(to_bund_err)
}
fn do_manuscript(vm: &mut VM) -> Result<&mut VM> {
let tag = "ink.export.manuscript";
let ctx = prologue(vm, tag)?;
let (meta, chapters) = model(&ctx)?;
let typ = crate::manuscript::build_typst(&meta, &chapters);
write_bytes(&ctx, tag, typ.as_bytes())?;
Ok(vm)
}
fn w_markdown(vm: &mut VM) -> BundResult<'_> {
do_markdown(vm).map_err(to_bund_err)
}
fn do_markdown(vm: &mut VM) -> Result<&mut VM> {
let tag = "ink.export.markdown";
let ctx = prologue(vm, tag)?;
let combined = combined(&ctx, tag)?;
crate::export::build_markdown(&combined)
.write_to(&ctx.path)
.map_err(|e| anyhow!("{tag}: write {}: {e}", ctx.path.display()))?;
Ok(vm)
}
fn w_tex(vm: &mut VM) -> BundResult<'_> {
do_tex(vm).map_err(to_bund_err)
}
fn do_tex(vm: &mut VM) -> Result<&mut VM> {
let tag = "ink.export.tex";
let ctx = prologue(vm, tag)?;
let combined = combined(&ctx, tag)?;
crate::export::build_tex(&combined)
.write_to(&ctx.path)
.map_err(|e| anyhow!("{tag}: write {}: {e}", ctx.path.display()))?;
Ok(vm)
}
fn w_epub(vm: &mut VM) -> BundResult<'_> {
do_epub(vm).map_err(to_bund_err)
}
fn do_epub(vm: &mut VM) -> Result<&mut VM> {
let tag = "ink.export.epub";
let ctx = prologue(vm, tag)?;
let combined = combined(&ctx, tag)?;
let md = crate::export::markdown::typst_to_markdown(&combined);
let title = crate::cli::epub::clean_title(&ctx.book.title);
crate::export::build_epub(&md, &title)
.map_err(|e| anyhow!("{tag}: {e}"))?
.write_to(&ctx.path)
.map_err(|e| anyhow!("{tag}: write {}: {e}", ctx.path.display()))?;
Ok(vm)
}