allow sispare-from-jisho to handle words with only readings
[sispare.git] / add_card.myr
blob4ea596ed4253d8e69265f2709af2df0601a14b51
1 use std
3 use bio
4 use date
5 use sys
7 use t
9 use "util"
11 const main = {args
12         var dir_arg : byte[:] = [][:]
13         var a_file_arg : byte[:] = [][:]
14         var b_file_arg : byte[:] = [][:]
15         var a_string_arg : byte[:] = [][:]
16         var b_string_arg : byte[:] = [][:]
17         var name_arg : byte[:] = [][:]
18         var basedir : byte[:] = [][:]
19         var dir_checked = false
20         var cmd : std.optparsed = std.optparse(args, &[
21                 .minargs = 0,
22                 .maxargs = 0,
23                 .opts = [
24                         [ .opt = 'd', .arg = "dir", .desc = "base directory", .optional = true ],
25                         [ .opt = 'n', .arg = "name", .desc = "card name (for uniqueness)", .optional = false ],
26                         [ .opt = 'a', .arg = "a-file", .desc = "file for A-side" ],
27                         [ .opt = 'A', .arg = "a-string", .desc = "string for A-side" ],
28                         [ .opt = 'b', .arg = "b-file", .desc = "file for B-side" ],
29                         [ .opt = 'B', .arg = "b-string", .desc = "string for B-side" ],
30                 ][:],
31         ])
33         for opt : cmd.opts
34                 match opt
35                 | ('d', dir): dir_arg = dir
36                 | ('n', name): name_arg = name
37                 | ('a', arg): a_file_arg = arg
38                 | ('b', arg): b_file_arg = arg
39                 | ('A', arg): a_string_arg = arg
40                 | ('B', arg): b_string_arg = arg
41                 | _: std.fatal("impossible {}\n", opt)
42                 ;;
43         ;;
45         match get_basedir(dir_arg)
46         | `std.Err e: std.fatal("{}\n", e)
47         | `std.Ok b: basedir = b
48         ;;
49         auto (basedir : t.doomed_str)
51         if name_arg.len == 0
52                 std.fatal("-n is required\n")
53         elif (a_file_arg.len == 0) && (a_string_arg.len == 0)
54                 std.fatal("One of -a or -A is required\n")
55         elif (a_file_arg.len != 0) && (a_string_arg.len != 0)
56                 std.fatal("Only one of -a or -A is permitted\n")
57         elif (b_file_arg.len == 0) && (b_string_arg.len == 0)
58                 std.fatal("One of -b or -B is required\n")
59         elif (b_file_arg.len != 0) && (b_string_arg.len != 0)
60                 std.fatal("Only one of -b or -B is permitted\n")
61         ;;
63         match choose_arg(a_string_arg, a_file_arg)
64         | `std.Ok str: a_string_arg = str
65         | `std.Err e: std.fatal("{}\n", e)
66         ;;
68         match choose_arg(b_string_arg, b_file_arg)
69         | `std.Ok str: b_string_arg = str
70         | `std.Err e: std.fatal("{}\n", e)
71         ;;
73         var card_dir : byte[:] = std.pathjoin([basedir, "cards", name_arg][:])
75         match std.diropen(card_dir)
76         | `std.Ok d2:
77                 std.dirclose(d2)
78                 std.fatal("card \"{}\" already exists\n", name_arg)
79         | `std.Err e:
80         ;;
82         match std.mkpath(card_dir)
83         | std.Enone:
84         | e: std.fatal("mkdir(\"{}\"): {}\n", card_dir, e)
85         ;;
87         var sched_dir_path = std.pathjoin([basedir, "schedule"][:])
88         match std.mkpath(sched_dir_path)
89         | std.Enone:
90         | e: std.fatal("mkdir(\"{}\"): {}\n", sched_dir_path, e)
91         ;;
93         var a_path : byte[:] = std.pathjoin([card_dir, "side_A"][:])
94         var b_path : byte[:] = std.pathjoin([card_dir, "side_B"][:])
95         var level_path : byte[:] = std.pathjoin([card_dir, "level"][:])
96         var next_review : date.instant = date.subperiod(date.utcnow(), `date.Day 7)
97         var sched_path = std.pathjoin([sched_dir_path, std.fmt("{f=%Y-%m-%d}", next_review)][:])
99         match std.open(sched_path, std.Owrite | std.Ocreat | std.Oappend)
100         | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", sched_path, e)
101         | `std.Ok fd:
102                 std.fput(fd, "{}\n", name_arg)
103                 std.close(fd)
104         ;;
106         match std.open(level_path, std.Owrite | std.Ocreat | std.Oappend)
107         | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", level_path, e)
108         | `std.Ok fd:
109                 std.fput(fd, "1\n")
110                 std.close(fd)
111         ;;
113         match std.open(a_path, std.Owrite | std.Ocreat | std.Oappend)
114         | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", a_path, e)
115         | `std.Ok fd:
116                 std.fput(fd, "{}", a_string_arg)
117                 std.close(fd)
118         ;;
120         match std.open(b_path, std.Owrite | std.Ocreat | std.Oappend)
121         | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", b_path, e)
122         | `std.Ok fd:
123                 std.fput(fd, "{}", b_string_arg)
124                 std.close(fd)
125         ;;
128 const choose_arg = {str : byte[:], path : byte[:]
129         if str.len > 0
130                 -> `std.Ok std.fmt("{}\n", str)
131         ;;
133         match std.slurp(path)
134         | `std.Ok buf:
135                 if buf.len > 0
136                         -> `std.Ok buf
137                 ;;
139                 -> `std.Err std.fmt("file \"{}\" seems empty", path)
140         | `std.Err e:
141                 -> `std.Err std.fmt("std.slurp(\"{}\"): {}", path, e)
142         ;;