From 6470d2ef985ede9ee1e7bce42f70ce8c6cd68d3d Mon Sep 17 00:00:00 2001 From: "S. Gilles" Date: Sun, 26 Jul 2020 15:02:06 -0400 Subject: [PATCH] implement sispare-add-card --- README | 6 ++- add_card.myr | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bld.proj | 4 +- sispare.myr | 82 ++++++++-------------------------- util.myr | 51 +++++++++++++++++++++ 5 files changed, 221 insertions(+), 65 deletions(-) create mode 100644 add_card.myr create mode 100644 util.myr diff --git a/README b/README index 02445ba..1f011ff 100644 --- a/README +++ b/README @@ -50,4 +50,8 @@ is reset to 1. To add things, you can run - - sispare-add-card [ -d ] [ -a | -A ] [ -b | -B ] + - sispare-add-card + [ -d ] + -n # For uniqueness + [ -a | -A ] # One is required + [ -b | -B ] # One is required diff --git a/add_card.myr b/add_card.myr new file mode 100644 index 0000000..4ea596e --- /dev/null +++ b/add_card.myr @@ -0,0 +1,143 @@ +use std + +use bio +use date +use sys + +use t + +use "util" + +const main = {args + var dir_arg : byte[:] = [][:] + var a_file_arg : byte[:] = [][:] + var b_file_arg : byte[:] = [][:] + var a_string_arg : byte[:] = [][:] + var b_string_arg : byte[:] = [][:] + var name_arg : byte[:] = [][:] + var basedir : byte[:] = [][:] + var dir_checked = false + var cmd : std.optparsed = std.optparse(args, &[ + .minargs = 0, + .maxargs = 0, + .opts = [ + [ .opt = 'd', .arg = "dir", .desc = "base directory", .optional = true ], + [ .opt = 'n', .arg = "name", .desc = "card name (for uniqueness)", .optional = false ], + [ .opt = 'a', .arg = "a-file", .desc = "file for A-side" ], + [ .opt = 'A', .arg = "a-string", .desc = "string for A-side" ], + [ .opt = 'b', .arg = "b-file", .desc = "file for B-side" ], + [ .opt = 'B', .arg = "b-string", .desc = "string for B-side" ], + ][:], + ]) + + for opt : cmd.opts + match opt + | ('d', dir): dir_arg = dir + | ('n', name): name_arg = name + | ('a', arg): a_file_arg = arg + | ('b', arg): b_file_arg = arg + | ('A', arg): a_string_arg = arg + | ('B', arg): b_string_arg = arg + | _: std.fatal("impossible {}\n", opt) + ;; + ;; + + match get_basedir(dir_arg) + | `std.Err e: std.fatal("{}\n", e) + | `std.Ok b: basedir = b + ;; + auto (basedir : t.doomed_str) + + if name_arg.len == 0 + std.fatal("-n is required\n") + elif (a_file_arg.len == 0) && (a_string_arg.len == 0) + std.fatal("One of -a or -A is required\n") + elif (a_file_arg.len != 0) && (a_string_arg.len != 0) + std.fatal("Only one of -a or -A is permitted\n") + elif (b_file_arg.len == 0) && (b_string_arg.len == 0) + std.fatal("One of -b or -B is required\n") + elif (b_file_arg.len != 0) && (b_string_arg.len != 0) + std.fatal("Only one of -b or -B is permitted\n") + ;; + + match choose_arg(a_string_arg, a_file_arg) + | `std.Ok str: a_string_arg = str + | `std.Err e: std.fatal("{}\n", e) + ;; + + match choose_arg(b_string_arg, b_file_arg) + | `std.Ok str: b_string_arg = str + | `std.Err e: std.fatal("{}\n", e) + ;; + + var card_dir : byte[:] = std.pathjoin([basedir, "cards", name_arg][:]) + + match std.diropen(card_dir) + | `std.Ok d2: + std.dirclose(d2) + std.fatal("card \"{}\" already exists\n", name_arg) + | `std.Err e: + ;; + + match std.mkpath(card_dir) + | std.Enone: + | e: std.fatal("mkdir(\"{}\"): {}\n", card_dir, e) + ;; + + var sched_dir_path = std.pathjoin([basedir, "schedule"][:]) + match std.mkpath(sched_dir_path) + | std.Enone: + | e: std.fatal("mkdir(\"{}\"): {}\n", sched_dir_path, e) + ;; + + var a_path : byte[:] = std.pathjoin([card_dir, "side_A"][:]) + var b_path : byte[:] = std.pathjoin([card_dir, "side_B"][:]) + var level_path : byte[:] = std.pathjoin([card_dir, "level"][:]) + var next_review : date.instant = date.subperiod(date.utcnow(), `date.Day 7) + var sched_path = std.pathjoin([sched_dir_path, std.fmt("{f=%Y-%m-%d}", next_review)][:]) + + match std.open(sched_path, std.Owrite | std.Ocreat | std.Oappend) + | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", sched_path, e) + | `std.Ok fd: + std.fput(fd, "{}\n", name_arg) + std.close(fd) + ;; + + match std.open(level_path, std.Owrite | std.Ocreat | std.Oappend) + | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", level_path, e) + | `std.Ok fd: + std.fput(fd, "1\n") + std.close(fd) + ;; + + match std.open(a_path, std.Owrite | std.Ocreat | std.Oappend) + | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", a_path, e) + | `std.Ok fd: + std.fput(fd, "{}", a_string_arg) + std.close(fd) + ;; + + match std.open(b_path, std.Owrite | std.Ocreat | std.Oappend) + | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", b_path, e) + | `std.Ok fd: + std.fput(fd, "{}", b_string_arg) + std.close(fd) + ;; +} + +const choose_arg = {str : byte[:], path : byte[:] + if str.len > 0 + -> `std.Ok std.fmt("{}\n", str) + ;; + + match std.slurp(path) + | `std.Ok buf: + if buf.len > 0 + -> `std.Ok buf + ;; + + -> `std.Err std.fmt("file \"{}\" seems empty", path) + | `std.Err e: + -> `std.Err std.fmt("std.slurp(\"{}\"): {}", path, e) + ;; +} diff --git a/bld.proj b/bld.proj index 719bf70..bb7a2f6 100644 --- a/bld.proj +++ b/bld.proj @@ -1,7 +1,9 @@ bin sispare = sispare.myr + util.myr ;; -sispare-add-card = +bin sispare-add-card = add_card.myr + util.myr ;; diff --git a/sispare.myr b/sispare.myr index 22d56ae..e3078ea 100644 --- a/sispare.myr +++ b/sispare.myr @@ -8,9 +8,19 @@ use sys use t use termdraw +use "util" + const wait_days : int[:] = [ 0, 1, 2, 3, 5, 8, 13, 21, 34, 55 ][:] +const fg_plain = `termdraw.RGB (0x20, 0x1b, 0x0c) +const fg_lighter = `termdraw.RGB (0x60, 0x5b, 0x5c) +const fg_bluer = `termdraw.RGB (0x1b, 0x28, 0x70) +const bg_plain = `termdraw.RGB (0xb2, 0xad, 0x99) +const bg_alt = `termdraw.RGB (0xa4, 0x9d, 0x88) +const bg_alt2 = `termdraw.RGB (0x4e, 0x4a, 0x42) + const main = {args + var dir_arg : byte[:] = [][:] var basedir : byte[:] = [][:] var dir_checked = false var cmd : std.optparsed = std.optparse(args, &[ @@ -23,60 +33,14 @@ const main = {args for opt : cmd.opts match opt - | ('d', dir): basedir = std.sldup(dir) + | ('d', dir): dir_arg = dir | _: std.fatal("impossible {}\n", opt) ;; ;; - /* Try to open the XDG one */ - if basedir.len == 0 - match std.getenv("XDG_DATA_HOME") - | `std.None: - | `std.Some d: basedir = std.fmt("{}/sispare", d) - ;; - - if basedir.len != 0 - match std.diropen(basedir) - | `std.Ok d2: - dir_checked = true - std.dirclose(d2) - | `std.Err _: - std.slfree(basedir) - basedir = [][:] - ;; - ;; - ;; - - /* Try to open the $HOME one */ - if basedir.len == 0 - var d : byte[:] = fileutil.homedir() - if d.len != 0 - basedir = std.fmt("{}/.sispare", d) - ;; - - if basedir.len != 0 - match std.diropen(basedir) - | `std.Ok d2: - dir_checked = true - std.dirclose(d2) - | `std.Err _: - std.slfree(basedir) - basedir = [][:] - ;; - ;; - ;; - - /* Make sure we've got one. */ - if !dir_checked - if basedir.len == 0 - std.fatal("cannot find any directory; make or specify one.\n") - ;; - - match std.diropen(basedir) - | `std.Ok d2: std.dirclose(d2) - | `std.Err e: - std.fatal("std.diropen(\"{}\"): {}\n", basedir, e) - ;; + match get_basedir(dir_arg) + | `std.Err e: std.fatal("{}\n", e) + | `std.Ok b: basedir = b ;; auto (basedir : t.doomed_str) @@ -186,11 +150,11 @@ const do_session = {d : byte[:] ;; var when = date.addperiod(now, `date.Day wait_days[std.max(0, level)]) - var schedpath = std.fmt("{}/schedule/{f=%Y-%m-%d}", d, when) - auto (schedpath : t.doomed_str) + var sched_path = std.fmt("{}/schedule/{f=%Y-%m-%d}", d, when) + auto (sched_path : t.doomed_str) - match std.open(schedpath, std.Owrite | std.Ocreat | std.Oappend) - | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", schedpath, e) + match std.open(sched_path, std.Owrite | std.Ocreat | std.Oappend) + | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", sched_path, e) | `std.Ok sfd: std.fput(sfd, "{}\n", card) std.close(sfd) @@ -201,6 +165,7 @@ const do_session = {d : byte[:] /* delete the old lists */ for f : files_to_rm + std.put("removing {}\n", f) std.remove(f) ;; } @@ -379,15 +344,6 @@ const print_progress = {t : termdraw.term#, s : byte[:] termdraw.put(t, s) } - -const fg_plain = `termdraw.RGB (0x20, 0x1b, 0x0c) -const fg_lighter = `termdraw.RGB (0x60, 0x5b, 0x5c) -const bg_plain = `termdraw.RGB (0xb2, 0xad, 0x99) -const bg_alt = `termdraw.RGB (0xa4, 0x9d, 0x88) -const fg_bluer = `termdraw.RGB (0x1b, 0x28, 0x70) -const bg_alt2 = `termdraw.RGB (0x4e, 0x4a, 0x42) - - const split_by_cell_width = {base : byte[:], width : std.size var position : std.size = 0 var cur_width : std.size = 0 diff --git a/util.myr b/util.myr new file mode 100644 index 0000000..38f0aa3 --- /dev/null +++ b/util.myr @@ -0,0 +1,51 @@ +use std +use fileutil + +pkg = + const get_basedir : (explicitly_provided : byte[:] -> std.result(byte[:], byte[:])) +;; + +const get_basedir = { explicitly_provided : byte[:] + var try_dir : byte[:] = [][:] + + if explicitly_provided.len > 0 + match std.diropen(explicitly_provided) + | `std.Ok d2: + std.dirclose(d2) + -> `std.Ok std.sldup(explicitly_provided) + | `std.Err e: + -> `std.Err std.fmt("dtd.diropen(\"{}\"): {}", explicitly_provided, e) + ;; + ;; + + /* Try to open the XDG one */ + + match std.getenv("XDG_DATA_HOME") + | `std.None: + | `std.Some d: + try_dir = std.pathjoin([d, "sispare"][:]) + match std.diropen(try_dir) + | `std.Ok d2: + std.dirclose(d2) + -> `std.Ok try_dir + | `std.Err _: + ;; + ;; + + std.slfree(try_dir) + try_dir = [][:] + + /* Try to open the $HOME one */ + try_dir = std.pathjoin([ fileutil.homedir(), ".sispare" ][:]) + match std.diropen(try_dir) + | `std.Ok d2: + std.dirclose(d2) + -> `std.Ok try_dir + | `std.Err _: + ;; + + std.slfree(try_dir) + try_dir = [][:] + + -> `std.Err std.fmt("cannot open any directories; make or provide one") +} -- 2.11.4.GIT