~bmp/hayom

ref: a6e57695974770dbecd4c2d88aaf4c3288092feb hayom/cli.ts -rw-r--r-- 2.6 KiB
a6e57695Benjamin Pollack support Windows 6 months ago
                                                                                
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
99
100
101
102
103
104
105
106
107
108
109
110
111
import { DateTime, parseDate } from "./deps.ts";
import { flags } from "./deps.ts";
import { loadConfig } from "./config.ts";
import {
  Entry,
  filterEntries,
  loadEntries,
  makeEntry,
  printEntry,
  saveEntries,
} from "./mod.ts";

function tryDate(d?: string): DateTime | undefined {
  return d ? DateTime.fromJSDate(parseDate(d)) : undefined;
}

function readEntries(entries: Entry[], opts: flags.Args) {
  let from = tryDate(opts.from);
  let to = tryDate(opts.to);
  if (opts.on) {
    const on = tryDate(opts.on);
    if (typeof on !== "object") {
      throw new Error("Bad date");
    }
    [from, to] = [on.startOf("day"), on.endOf("day")];
  }
  const tags = opts._.filter((arg) =>
    typeof arg === "string" && arg.match(/^@./)
  ) as string[];
  const limit = opts.limit;

  const filtered = filterEntries(
    entries,
    {
      from,
      to,
      limit,
      tags,
    },
  );
  let first = true;
  for (const entry of filtered) {
    if (!first && !opts.summary) {
      console.log();
    }
    first = false;
    printEntry(entry, opts.summary);
  }
}

async function edit(body: string, editor: string[]) {
  const temp = Deno.makeTempFileSync({ suffix: ".jrnl" });
  try {
    Deno.writeTextFileSync(temp, body);
    const proc = Deno.run({ cmd: [...editor, temp] });
    const status = await proc.status();
    if (status.success) {
      return Deno.readTextFileSync(temp);
    }
  } finally {
    Deno.remove(temp);
  }
}

async function main() {
  const args = [...Deno.args];
  const config = loadConfig();

  let path;
  if (args.length > 0 && Object.hasOwn(config.journals, args[0])) {
    path = config.journals[args[0]].journal;
    args.shift();
  } else {
    path = config.journals[config.default].journal;
  }

  try {
    Deno.lstatSync(path);
  } catch (e) {
    if (e instanceof Deno.errors.NotFound) {
      Deno.createSync(path).close();
    } else throw e;
  }

  const entries = loadEntries(path);
  const opts = flags.parse(args, {
    boolean: ["summary"],
    alias: { "s": ["summary"], "f": ["from"], "t": ["to"], "n": ["count"] },
  });

  if (
    ["from", "f", "to", "t", "on", "count", "n"].some((arg) => arg in opts) ||
    (opts._.length > 0 &&
      opts._.every((e) => typeof e === "string" && e[0] === "@"))
  ) {
    readEntries(entries, opts);
  } else {
    const rawEntry = opts._.length > 0
      ? opts._.join(" ")
      : await edit("", config.editor.split(/\s/));
    if (rawEntry && rawEntry.trim() !== "") {
      const entry = makeEntry(rawEntry);
      entries.push(entry);
      saveEntries(path, entries);
    }
  }
}

if (import.meta.main) {
  main();
}