diff options
| author | Julian Hurst <ark@mansus.space> | 2024-11-15 01:14:07 +0100 |
|---|---|---|
| committer | Julian Hurst <ark@mansus.space> | 2024-11-15 01:14:07 +0100 |
| commit | 8c3b227ba34cd2248396c3caaf35aef728914c48 (patch) | |
| tree | 21a996f4801667c861e355009e388ede269f3c15 | |
| parent | 4792b321eb3bf7b58fdddcc101a15bd83dad5d0f (diff) | |
| download | hatask-8c3b227ba34cd2248396c3caaf35aef728914c48.tar.gz | |
Better subcommand support, ids for tasks and ignore case
| -rw-r--r-- | cmd.ha | 151 | ||||
| -rw-r--r-- | hatask.ha | 38 |
2 files changed, 137 insertions, 52 deletions
@@ -6,101 +6,162 @@ use io; use path; use os::exec; use strconv; +use ascii; -type error = !(io::error | path::error | exec::error); +type error = !(!str | io::error | path::error | exec::error | strconv::error); -type func = fn(tasks: []task, args: str...) (void | task | error); +type func = fn(tasks: []task, args: arguments) (void | task | error); type command = struct { names: []str, + help: []getopt::help, func: *func, }; +type arguments = *getopt::command; + const PADDING: size = 30z; const commands: [_]command = [ command { names = ["f", "filter"], + help = ["<name>"], func = &filter, }, command { names = ["s", "show"], + help = ["<id>"], func = &show, }, command { names = ["w", "write"], + help = ["<id>"], func = &write, }, ]; -fn execcommand(name: str, tasks: []task, args: str...) (void | error) = { +fn execcommand(name: str, tasks: []task, args: arguments) (void | error) = { for (const c .. commands) { for (const n .. c.names) { if (n == name) { - c.func(tasks, args...)?; + c.func(tasks, args)?; }; }; }; }; -fn write(tasks: []task, args: str...) (void | task | error) = { - const cmdname = "task write"; - const cmdhelp: []getopt::help = ["write a task", "<name>"]; - if (len(args) == 0z) { - getopt::printhelp(os::stderr, cmdname, cmdhelp)?; - return; - }; - const cmd = getopt::parse(args, - cmdname: getopt::help, - cmdhelp[0], - cmdhelp[1], - ); - defer getopt::finish(&cmd); - +fn write(tasks: []task, a: arguments) (void | task | error) = { + const args = a.args; if (len(args) != 1z) { - getopt::printhelp(os::stderr, cmdname, cmdhelp)?; + getopt::printhelp(os::stderr, "write", a.help)?; return; }; - let buf = path::init()?; - const p = path::push(&buf, "tasks", args[0])?; - const c = exec::cmd("vim", p)?; + const id = strconv::stoz(args[0])?; + const t = if (len(tasks) > id) { + yield tasks[id]; + } else { + return "No such task"; + }; + const c = exec::cmd("vim", t.path)?; exec::exec(&c); }; -fn show(tasks: []task, args: str...) (void | task | error) = { - for (const t .. tasks) { - for (const s .. args) { - if (t.name == s) { - fmt::println(t.content)!; - }; - }; +fn show(tasks: []task, a: arguments) (void | task | error) = { + const args = a.args; + const id = strconv::stoz(args[0])?; + const t = if (len(tasks) > id) { + yield tasks[id]; + } else { + return "No such task"; }; + fmt::println(t.content)!; }; -fn printtask(t: task) (void | error) = { - const pad = PADDING - len(t.name) + len(strconv::utos(t.priority)); - fmt::printfln("{}{%}", t.name, t.priority, &fmt::mods { - pad = ' ', - width = pad, ... - })!; +fn printtask(t: task, id: size) (void | error) = { + let name = strings::dup(t.name); + defer free(name); + if (len(t.name) > PADDING - 4) { + name = strings::concat(strings::sub(t.name, 0z, PADDING - 4), + "..."); + }; + const pad = PADDING - len(name) + len(strconv::utos(t.priority)); + const namepad = 10 - len(strconv::ztos(id)) + len(name); + fmt::printfln("{}{%}{%}", id, name, &fmt::mods { + pad = ' ', + width = namepad, + ... + }, + t.priority, &fmt::mods { + pad = ' ', + width = pad, + ... + }, + )!; }; -fn filter(tasks: []task, args: str...) (void | task | error) = { +fn filter(tasks: []task, a: arguments) (void | task | error) = { const headpad = PADDING - len("name") + len("priority"); - fmt::printfln("name{%}", "priority", &fmt::mods { - pad = ' ', - width = headpad, - ... - })!; - for (const t .. tasks) { + const namepad = 10 - len("id") + len("name"); + fmt::printfln("id{%}{%}", + "name", &fmt::mods { + pad = ' ', + width = namepad, + ... + },"priority", &fmt::mods { + pad = ' ', + width = headpad, + ... + }, + )!; + const args = a.args; + for (let i = 0z; i < len(tasks); i += 1) { + const t = tasks[i]; if (len(args) == 0z) { - printtask(t)?; + printtask(t, i)?; }; for (const s .. args) { - if (strings::contains(t.name, s)) { - printtask(t)?; + const lname = ascii::strlower(t.name); + defer free(lname); + const ls = ascii::strlower(s); + defer free(ls); + if (strings::contains(lname, ls)) { + printtask(t, i)?; }; }; }; }; + +fn listall(tasks: []task) void = { + const headpad = PADDING - len("name") + len("priority"); + const namepad = 10 - len("id") + len("name"); + fmt::printfln("id{%}{%}", + "name", &fmt::mods { + pad = ' ', + width = namepad, + ... + },"priority", &fmt::mods { + pad = ' ', + width = headpad, + ... + }, + )!; + for (let i = 0z; i < len(tasks); i += 1) { + printtask(tasks[i], i)!; + }; +}; + +fn strerror(e: error) str = { + match (e) { + case let e: !str => + return e; + case let e: io::error => + return io::strerror(e); + case let e: path::error => + return path::strerror(e); + case let e: exec::error => + return exec::strerror(e); + case let e: strconv::error => + return strconv::strerror(e); + }; +}; @@ -11,9 +11,11 @@ use format::ini; use strconv; use encoding::utf8; use getopt; +use sort; type task = struct { name: str, + path: str, context: (str | void), tags: ([]str | void), priority: uint, @@ -88,12 +90,14 @@ fn readtask(taskpath: str) (task | rtaskerror) = { if (t.name == "") { t.name = strings::dup(path::basename(taskpath)); }; + t.path = strings::dup(taskpath); t.content = strings::dup(strings::trim(memio::string(&content)?)); return t; }; fn freetask(t: *task) void = { free(t.name); + free(t.path); free(t.content); if (t.context is str) { free(t.context as str); @@ -109,18 +113,24 @@ fn finishall(tasks: []task) void = { }; }; +fn sortname(a: const *opaque, b: const *opaque) int = { + const a = a: *task; + const b = b: *task; + return strings::compare(a.name, b.name); +}; + export fn main() void = { const cmd = getopt::parse(os::args, "tasklist", - "cmd", + ("filter", ["filter tasks", "id"]: []getopt::help), + ("f", ["filter tasks", "id"]: []getopt::help), + ("show", ["show task details", "id"]: []getopt::help), + ("s", ["show task details", "id"]: []getopt::help), + ("write", ["write a task", "id"]: []getopt::help), + ("w", ["write a task", "id"]: []getopt::help), ); defer getopt::finish(&cmd); - const com: []str = if (len(cmd.args) > 0) { - yield cmd.args; - } else { - yield ["filter"]; - }; const tasks = match (listtasks()) { case let e: fs::error => @@ -130,7 +140,21 @@ export fn main() void = { case let tasks: []task => yield tasks; }; + sort::sort(tasks: []opaque, size(task), &sortname); defer finishall(tasks); - execcommand(com[0], tasks, com[1..]...)!; + const com: (str, *getopt::command) = match (cmd.subcmd) { + case void => + listall(tasks); + return; + case let subcmd: (str, *getopt::command) => + yield (subcmd.0, subcmd.1); + }; + + match (execcommand(com.0, tasks, com.1)) { + case let e: error => + fmt::fatal(strerror(e)); + case => + void; + }; }; |
