use fmt; use ev; use getopt; use os; use time; use strings; use strconv; use math; type uniterror = !void; fn timer_handler(file: *ev::file) void = { const loop = ev::getloop(file); const t = ev::getuser(file): *time::instant; const t = *t; if (print_countdown(t)) { ev::stop(loop); }; }; fn print_countdown(instant: time::instant) bool = { const tn = time::now(time::clock::MONOTONIC); const currentduration = time::diff(tn, instant); // man page console_codes(4) is useful fmt::fprint(os::stderr, "\x1B[1K\r")!; const count = human_readable(currentduration); defer free(count); fmt::fprint(os::stderr, count)!; return currentduration <= 0; }; // Transform a duration to a human readable string representation. fn human_readable(duration: time::duration) str = { const hours = if (duration > time::HOUR) { let hours = math::floorf64(duration: f64 / time::HOUR: f64): u64; duration = (duration % time::HOUR); yield hours; } else { yield 0u64; }; const mins = if (duration > time::MINUTE) { let mins = math::floorf64(duration: f64 / time::MINUTE: f64): u64; duration = (duration % time::MINUTE); yield mins; } else { yield 0u64; }; const secs = if (duration > time::SECOND) { yield math::floorf64(duration: f64 / time::SECOND: f64): u64; } else { yield 0u64; }; const mods = &fmt::mods { pad = '0', width = 2, ... }; return fmt::asprintf("{%}:{%}:{%}", hours, mods, mins, mods, secs, mods); }; fn parse_duration(duration: const str) (time::duration | uniterror | strconv::invalid | strconv::overflow) = { const s = strings::toutf8(duration); const unit = s[len(s)-1]; const unit = switch (unit) { case 's' => yield time::SECOND; case 'm' => yield time::MINUTE; case 'h' => yield time::HOUR; case => return uniterror; }; const d = strconv::stoi(strings::sub(duration, 0, len(duration) - 1))?; return d * unit; }; export fn main() void = { const cmd = getopt::parse(os::args, "timer for alarms", "duration", ); defer getopt::finish(&cmd); if (len(cmd.args) != 1) { getopt::printusage(os::stderr, os::args[0], cmd.help)!; os::exit(os::status::FAILURE); }; const duration = cmd.args[0]; const duration = parse_duration(duration)!; const loop = ev::newloop()!; const t = time::now(time::clock::MONOTONIC); const t = time::add(t, duration); defer ev::finish(&loop); const f = ev::newtimer(&loop, &timer_handler, time::clock::MONOTONIC)!; ev::setuser(f, &t); print_countdown(t); ev::timer_configure(f, 1 * time::SECOND, 1 * time::SECOND); for (ev::dispatch(&loop, -1)!) void; fmt::println()!; };