main.zig (5005B)
1 const command = @import("command.zig"); 2 const options = @import("options.zig"); 3 const pausable_timer = @import("pausable_timer.zig"); 4 const std = @import("std"); 5 const builtin = @import("builtin"); 6 const step = @import("step.zig"); 7 8 pub fn main(init: std.process.Init) !void { 9 const allocator = init.gpa; 10 11 const program_and_args = try init.minimal.args.toSlice(init.arena.allocator()); 12 13 const program = program_and_args[0]; 14 const program_args = program_and_args[1..]; 15 16 var stdout_buffer: [1024]u8 = undefined; 17 var stdout_writer = std.Io.File.stdout().writer(init.io, &stdout_buffer); 18 const stdout = &stdout_writer.interface; 19 20 const opts = options.parseArgs(program_args) catch { 21 try printUsage(allocator, stdout, program); 22 std.process.exit(1); 23 }; 24 25 switch (opts) { 26 .help => { 27 try printUsage(allocator, stdout, program); 28 }, 29 .settings => |settings| { 30 try run(init, allocator, stdout, settings); 31 }, 32 } 33 } 34 35 fn run(init: std.process.Init, allocator: std.mem.Allocator, out: *std.Io.Writer, settings: options.Settings) !void { 36 var poller = try command.Poller.init(allocator); 37 defer poller.deinit(); 38 39 const status_file: ?std.Io.File = if (settings.file) |path| try std.Io.Dir.cwd().createFile(init.io, path, .{}) else null; 40 defer if (status_file) |f| f.close(init.io); 41 defer if (settings.file) |path| std.Io.Dir.cwd().deleteFile(init.io, path) catch |err| { 42 std.log.debug("failed to delete status file: {any}", .{err}); 43 }; 44 45 if (poller.interactive()) { 46 try out.print("\n", .{}); 47 try out.flush(); 48 } 49 50 var current = step.Step{}; 51 while (true) { 52 var timer = pausable_timer.PausableTimer.init(init.io); 53 54 var remaining = current.length(settings); 55 56 while (remaining > 0) : ({ 57 remaining = current.length(settings) -| timer.read(init.io); 58 }) { 59 if (poller.interactive()) { 60 try out.print("\x1b[F\x1b[2K\r", .{}); 61 try out.flush(); 62 } 63 try printStatus( 64 allocator, 65 out, 66 status_file, 67 init.io, 68 current, 69 remaining, 70 ); 71 72 if (try poller.pollTimeout(std.time.ns_per_s)) |command_| { 73 switch (command_) { 74 command.Command.Quit => { 75 try out.print("\n", .{}); 76 try out.flush(); 77 return; 78 }, 79 command.Command.Pause => { 80 timer.togglePause(init.io); 81 }, 82 command.Command.Reset => { 83 if (timer.paused and remaining == current.length(settings)) { 84 current = step.Step{}; 85 } 86 87 timer.reset(init.io); 88 }, 89 } 90 } 91 } 92 93 current = current.next(); 94 if (settings.notifications and current.length(settings) > 0) { 95 notify(init.io, allocator, current.step_type.message()); 96 } 97 } 98 } 99 100 fn notify(io: std.Io, allocator: std.mem.Allocator, message: []const u8) void { 101 if (builtin.os.tag == .macos) { 102 const script = std.fmt.allocPrint(allocator, "display notification \"{s}\" with title \"pracomer\"", .{message}) catch return; 103 defer allocator.free(script); 104 const argv = [_][]const u8{ "osascript", "-e", script }; 105 var proc = std.process.spawn(io, .{ 106 .argv = &argv, 107 }) catch return; 108 _ = proc.wait(io) catch |err| { 109 std.log.debug("failed to wait for process: {any}", .{err}); 110 }; 111 } else { 112 const argv = [_][]const u8{ "notify-send", "-u", "critical", message }; 113 var proc = std.process.spawn(io, .{ 114 .argv = &argv, 115 }) catch return; 116 _ = proc.wait(io) catch |err| { 117 std.log.debug("failed to wait for process: {any}", .{err}); 118 }; 119 } 120 } 121 122 fn printStatus( 123 allocator: std.mem.Allocator, 124 out: *std.Io.Writer, 125 status_file: ?std.Io.File, 126 io: std.Io, 127 current: step.Step, 128 remaining: u64, 129 ) !void { 130 const msg = try current.render(allocator, remaining); 131 defer allocator.free(msg); 132 133 try out.print("{s}\n", .{msg}); 134 try out.flush(); 135 136 if (status_file) |f| { 137 var buf: [1024]u8 = undefined; 138 var fw = f.writer(io, &buf); 139 try fw.seekTo(0); 140 try fw.interface.writeAll(msg); 141 try fw.interface.writeAll("\n"); 142 try fw.flush(); 143 } 144 } 145 146 fn printUsage( 147 allocator: std.mem.Allocator, 148 out: *std.Io.Writer, 149 program: []const u8, 150 ) !void { 151 const usage = try options.usage(allocator, program); 152 defer allocator.free(usage); 153 154 try out.print("{s}", .{usage}); 155 try out.flush(); 156 } 157 158 test "main.zig" { 159 std.testing.refAllDecls(@This()); 160 }