use anyhow::{anyhow, Context, Result};
use serde_json::Value;
use std::collections::HashMap;
use std::path::Path;
use super::{commands, split_page_meta, PreprocessMetadata, PAGE_META_LABEL, PAGE_SYNC_SELECTOR};
use crate::typst::model::LayoutPaths;
use crate::typst::paths::slash_path;
use crate::typst::run::{TypstInput, INPUT_MODE, INPUT_RESULTS, INPUT_SOURCE_DIR, INPUT_TARGET};
pub fn preprocess_metadata(
typst: &Path,
layout: &LayoutPaths,
input: &Path,
results_input: &str,
) -> Result<PreprocessMetadata> {
let paged = preprocess_metadata_for_target(typst, layout, input, results_input, "paged")?;
let html = preprocess_metadata_for_target(typst, layout, input, results_input, "html")?;
let setup = paged
.get("setup")
.cloned()
.ok_or_else(|| anyhow!("typst eval metadata output is missing `setup`"))?;
let setup_array = setup
.as_array()
.ok_or_else(|| anyhow!("typst eval setup output must be an array"))?;
let (setup_json, page_meta) = split_page_meta(setup_array)?;
let paged_chunks = paged
.get("chunks")
.cloned()
.ok_or_else(|| anyhow!("typst eval metadata output is missing `chunks`"))?;
let html_chunks = html
.get("chunks")
.cloned()
.ok_or_else(|| anyhow!("typst eval metadata output is missing `chunks`"))?;
Ok(PreprocessMetadata {
setup_json,
page_meta,
chunk_queries: vec![
serde_json::to_string(&paged_chunks)?,
serde_json::to_string(&html_chunks)?,
],
})
}
fn preprocess_metadata_for_target(
typst: &Path,
layout: &LayoutPaths,
input: &Path,
results_input: &str,
target: &str,
) -> Result<Value> {
let output = commands::typst_eval(
typst,
layout,
input,
&format!(
r#"(
setup: query(selector(<calepin-config>).or(<{PAGE_META_LABEL}>)),
chunks: query(raw.where(block: true).or(<calepin-fence-label>).or(<calepin-chunk>)),
)"#
),
target,
&[
TypstInput::new(INPUT_MODE, "query"),
TypstInput::new(INPUT_RESULTS, results_input),
TypstInput::new(INPUT_SOURCE_DIR, source_dir_input(layout)),
TypstInput::new(INPUT_TARGET, target),
],
)?;
serde_json::from_str(&output).context("failed to parse typst eval metadata output")
}
pub fn page_anchors(typst: &Path, layout: &LayoutPaths) -> Result<HashMap<String, usize>> {
let results_input = super::results_input(layout);
let output = commands::typst_eval(
typst,
layout,
&layout.render_input,
&format!(
r#"query({PAGE_SYNC_SELECTOR}).map(it => (
label: it.value.label,
page: it.value.page,
))"#
),
"paged",
&[
TypstInput::new(INPUT_MODE, "render"),
TypstInput::new(INPUT_RESULTS, results_input),
TypstInput::new(INPUT_SOURCE_DIR, source_dir_input(layout)),
TypstInput::new(INPUT_TARGET, "paged"),
],
)?;
let root: Value =
serde_json::from_str(&output).context("failed to parse typst eval page sync output")?;
super::parse_page_anchor_entries(&root)
}
fn source_dir_input(layout: &LayoutPaths) -> String {
layout
.input_rel
.parent()
.map(slash_path)
.unwrap_or_default()
}
#[cfg(test)]
mod tests {}