summaryrefslogtreecommitdiffhomepage
path: root/dlang/src/main.d
blob: fffbff047341b2a132cf1cc35f6c6b8d2208b25c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/++
  ocda-write: a sample writer over the abstraction.

  Usage: ocda-write --text|--md|--html|--epub|--typst <file> [lang]

  --epub writes a zip, so it takes a path to write to rather than
  stdout: ocda-write --epub <file> [lang] --out <file>.epub

  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 std.stdio;
import ocda.load;
import ocda.model;
import write.epub;
import write.html;
import write.markdown;
import write.text;
import write.typst;

int main(string[] args) {
  string format_;
  string out_path;
  /+ ↓ where the images are, relative to the file being written. Only markdown
       takes it: a .md names an image by path, and the path depends on where the
       .md is put, which is the caller's business and not the model's. +/
  string image_pth = "image";
  string[] rest;
  bool want_out = false;
  bool want_image_pth = false;
  foreach (a; args[1 .. $]) {
    if (want_out) { out_path = a; want_out = false; continue; }
    if (want_image_pth) { image_pth = a; want_image_pth = false; continue; }
    if      (a == "--text") { format_ = "text"; }
    else if (a == "--html") { format_ = "html"; }
    else if (a == "--epub") { format_ = "epub"; }
    else if (a == "--typst" || a == "--typ") { format_ = "typst"; }
    else if (a == "--md" || a == "--markdown") { format_ = "markdown"; }
    else if (a == "--out")  { want_out = true; }
    else if (a == "--image-prefix") { want_image_pth = true; }
    else if (a.length > 2 && a[0 .. 2] == "--") {
      stderr.writeln("ocda-write: unknown option ", a);
      return 2;
    } else { rest ~= a; }
  }
  if (format_.length == 0 || rest.length < 1) {
    stderr.writeln("usage: ocda-write --text|--md|--html|--epub|--typst"
      ~ " <file>.ssp|<file>.ocda.db|<file>.sisupod [lang]"
      ~ "\n       --md also takes --image-prefix <path> (default \"image\")");
    return 2;
  }
  string lang = (rest.length > 1) ? rest[1] : "";
  OcdaDocument doc;
  try {
    doc = load(rest[0], lang);
  } catch (Exception e) {
    stderr.writeln("error: ", e.msg);
    return 2;
  }
  if (format_ == "epub") {
    if (out_path.length == 0) {
      stderr.writeln("ocda-write --epub: --out <file>.epub is needed,"
        ~ " because an epub is a zip and stdout is not a file");
      return 2;
    }
    import std.file : write;
    write(out_path, writeEpub(doc, rest[0]));
    return 0;
  }
  if (format_ == "typst") {
    string typ = writeTypst(doc);
    if (out_path.length == 0) { stdout.write(typ); return 0; }
    /+ a .typ names its images by path, so they are written beside it +/
    import std.file : mkdirRecurse, write;
    import std.path : buildPath, dirName;
    import ocda.images : canGiveImages, imageBytes;
    write(out_path, typ);
    if (canGiveImages(rest[0])) {
      string img_dir = buildPath(out_path.dirName, "image");
      bool made = false;
      foreach (name; imagesNamed(doc)) {
        auto bytes = imageBytes(rest[0], name);
        if (bytes.length == 0) { continue; }
        if (!made) { mkdirRecurse(img_dir); made = true; }
        write(buildPath(img_dir, name), bytes);
      }
    }
    return 0;
  }
  if (format_ == "markdown") { stdout.write(writeMarkdown(doc, image_pth)); return 0; }
  stdout.write((format_ == "html") ? writeHtmlScroll(doc) : writeText(doc));
  return 0;
}