From a73c7bcaf997fddd3aa746104e930cef8b08a934 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 2 Oct 2019 16:30:12 -0300 Subject: [PATCH] add lib/std/progress.zig --- lib/std/progress.zig | 107 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 lib/std/progress.zig diff --git a/lib/std/progress.zig b/lib/std/progress.zig new file mode 100644 index 0000000000..1fc74ce3fb --- /dev/null +++ b/lib/std/progress.zig @@ -0,0 +1,107 @@ +const std = @import("std"); +const testing = std.testing; + +pub const PrintConfig = struct { + /// If the current node (and its children) should + /// print to stderr on update() + flag: bool = false, + + /// If all output should be suppressed instead + /// serves the same practical purpose as `flag` but supposed to be used + /// by separate parts of the user program. + suppress: bool = false, +}; + +pub const ProgressNode = struct { + completed_items: usize = 0, + total_items: usize, + + print_config: PrintConfig, + + // TODO maybe instead of keeping a prefix field, we could + // select the proper prefix at the time of update(), and if we're not + // in a terminal, we use warn("/r{}", lots_of_whitespace). + prefix: []const u8, + + /// Create a new progress node. + pub fn start( + parent_opt: ?ProgressNode, + total_items_opt: ?usize, + ) !ProgressNode { + + // inherit the last set print "configuration" from the parent node + var print_config = PrintConfig{}; + if (parent_opt) |parent| { + print_config = parent.print_config; + } + + var stderr = try std.io.getStdErr(); + const is_term = std.os.isatty(stderr.handle); + + // if we're in a terminal, use vt100 escape codes + // for the progress. + var prefix: []const u8 = undefined; + if (is_term) { + prefix = "\x21[2K\r"; + } else { + prefix = "\n"; + } + + return ProgressNode{ + .total_items = total_items_opt orelse 0, + .print_config = print_config, + .prefix = prefix, + }; + } + + /// Signal an update on the progress node. + /// The user of this function is supposed to modify + /// ProgressNode.PrintConfig.flag when update() is supposed to print. + pub fn update( + self: *ProgressNode, + current_action: ?[]const u8, + items_done_opt: ?usize, + ) void { + if (items_done_opt) |items_done| { + self.completed_items = items_done; + + if (items_done > self.total_items) { + self.total_items = items_done; + } + } + + var cfg = self.print_config; + if (cfg.flag and !cfg.suppress and current_action != null) { + std.debug.warn( + "{}[{}/{}] {}", + self.prefix, + self.completed_items, + self.total_items, + current_action, + ); + } + } + + pub fn end(self: *ProgressNode) void { + if (!self.print_config.flag) return; + + // TODO emoji? + std.debug.warn("\n[V] done!"); + } +}; + +test "basic functionality" { + var node = try ProgressNode.start(null, 100); + + var buf: [100]u8 = undefined; + + var i: usize = 0; + while (i < 100) : (i += 6) { + if (i > 50) node.print_config.flag = true; + const msg = try std.fmt.bufPrint(buf[0..], "action at i={}", i); + node.update(msg, i); + std.time.sleep(10 * std.time.millisecond); + } + + node.end(); +}