//// ocda-write: a sample writer over the abstraction. //// //// ocda-write --text|--html .ssp|.ocda.db|.zip [lang] //// //// The input may be either serialisation, and the writer is not told which. //// That is the claim being demonstrated: a writer is a function from the //// model to bytes, so a document renders the same whichever of its two //// abstraction files it is given. //// //// Output goes to stdout, so that the comparison against spine's own output //// is a shell pipeline and not a directory of files to clean up. import argv import gleam/list import gleam/string import ocda/images import gleam/io import ocda/load import simplifile import write/epub import write/html import write/markdown import write/text import write/typst pub fn main() { case argv.load().arguments { ["--text", path] -> render(path, "", "text") ["--text", path, lang] -> render(path, lang, "text") ["--html", path] -> render(path, "", "html") ["--html", path, lang] -> render(path, lang, "html") ["--epub", path, "--out", out] -> write_epub(path, "", out) ["--epub", path, lang, "--out", out] -> write_epub(path, lang, out) ["--md", path] -> render_markdown(path, "", "image") ["--md", path, lang] -> render_markdown(path, lang, "image") ["--md", path, "--image-prefix", pth] -> render_markdown(path, "", pth) ["--md", path, lang, "--image-prefix", pth] -> render_markdown(path, lang, pth) ["--typ", path] -> render(path, "", "typst") ["--typ", path, lang] -> render(path, lang, "typst") ["--typst", path] -> render(path, "", "typst") ["--typst", path, lang] -> render(path, lang, "typst") ["--typst", path, "--out", out] -> write_typst(path, "", out) ["--typst", path, lang, "--out", out] -> write_typst(path, lang, out) _ -> usage() } } fn usage() -> Nil { io.println_error( "usage: ocda-write --text|--html|--typst [lang]\n" <> " ocda-write --md [lang] [--image-prefix ]\n" <> " ocda-write --epub [lang] --out .epub", ) halt(2) } fn render(path: String, lang: String, format: String) -> Nil { case load.load(path, lang, False) { Error(message) -> { io.println_error("error: " <> message) halt(2) } Ok(doc) -> io.print(case format { "html" -> html.write(doc) "typst" -> typst.write(doc) _ -> text.write(doc) }) } } /// Markdown takes one thing the other formats do not: where the images are, /// relative to wherever the .md is put. It has its own entry for that, as the /// epub and the typst do for --out. fn render_markdown(path: String, lang: String, image_pth: String) -> Nil { case load.load(path, lang, False) { Error(message) -> { io.println_error("error: " <> message) halt(2) } Ok(doc) -> io.print(markdown.write(doc, image_pth)) } } /// An epub is a zip, so it goes to a file rather than to stdout. fn write_epub(path: String, lang: String, out: String) -> Nil { case load.load(path, lang, False) { Error(message) -> { io.println_error("error: " <> message) halt(2) } Ok(doc) -> case simplifile.write_bits(out, epub.write(doc, path)) { Ok(_) -> Nil Error(e) -> { io.println_error("error: " <> out <> ": " <> simplifile.describe_error(e)) halt(2) } } } } /// Gleam has no standard exit, and the exit status is what a sweep script /// reads. @external(erlang, "erlang", "halt") fn halt(code: Int) -> Nil /// A .typ names its images by path rather than carrying them, so when it goes /// to a file the images are written beside it. fn write_typst(path: String, lang: String, out: String) -> Nil { case load.load(path, lang, False) { Error(message) -> { io.println_error("error: " <> message) halt(2) } Ok(doc) -> { case simplifile.write(out, typst.write(doc)) { Ok(_) -> Nil Error(e) -> { io.println_error( "error: " <> out <> ": " <> simplifile.describe_error(e), ) halt(2) } } let dir = case string.split(out, "/") { [_] -> "image" parts -> string.join(list.take(parts, list.length(parts) - 1), "/") <> "/image" } case images.can_give_images(path) { False -> Nil True -> epub.images_named(doc) |> list.each(fn(name) { case images.image_bytes(path, name) { Ok(bits) -> { let _ = simplifile.create_directory_all(dir) let _ = simplifile.write_bits(dir <> "/" <> name, bits) Nil } Error(_) -> Nil } }) } } } }