mod css;
mod fence;
mod figure;
use std::error::Error;
use std::io::{BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use serde_json::Value;
fn main() -> Result<(), Box<dyn Error>> {
let argv: Vec<String> = std::env::args().collect();
if argv.get(1).map(String::as_str) == Some("supports") {
let html = argv.get(2).map(String::as_str) == Some("html");
std::process::exit(if html { 0 } else { 1 });
}
let mut raw = String::new();
std::io::stdin().read_to_string(&mut raw)?;
let mut payload: Vec<Value> = serde_json::from_str(&raw)?;
let mut book = payload.pop().ok_or("preprocessor input missing book")?;
let context = payload.pop().ok_or("preprocessor input missing context")?;
let src_root = src_root(&context);
let bundled_css = bundled_css(&context);
if let Some(sections) = sections_mut(&mut book) {
render(sections, &src_root, bundled_css);
}
let mut out = BufWriter::new(std::io::stdout().lock());
serde_json::to_writer(&mut out, &book)?;
out.flush()?;
Ok(())
}
fn sections_mut(book: &mut Value) -> Option<&mut Vec<Value>> {
let obj = book.as_object_mut()?;
let key = if obj.contains_key("sections") { "sections" } else { "items" };
obj.get_mut(key)?.as_array_mut()
}
fn src_root(context: &Value) -> PathBuf {
let root = context["root"].as_str().unwrap_or(".");
let src = context["config"]["book"]["src"].as_str().unwrap_or("src");
Path::new(root).join(src)
}
fn bundled_css(context: &Value) -> bool {
context["config"]["preprocessor"]["lini"]["bundled-css"].as_bool().unwrap_or(true)
}
fn render(items: &mut [Value], src_root: &Path, bundled_css: bool) {
for item in items {
let Some(chapter) = item.get_mut("Chapter").and_then(Value::as_object_mut) else {
continue;
};
let source_path =
chapter.get("source_path").and_then(Value::as_str).unwrap_or("<book>").to_owned();
let base_dir = src_root.join(&source_path).parent().map(Path::to_path_buf);
if let Some(content) = chapter.get("content").and_then(Value::as_str) {
let mut figures = 0;
let mut rendered = fence::rewrite(content, |source, line| {
figures += 1;
figure::render(source, &source_path, line, base_dir.as_deref())
});
if bundled_css && figures > 0 {
rendered.insert_str(0, css::style_tag());
}
chapter.insert("content".into(), Value::String(rendered));
}
if let Some(sub) = chapter.get_mut("sub_items").and_then(Value::as_array_mut) {
render(sub, src_root, bundled_css);
}
}
}