Commit a73c7bcaf9
Changed files (1)
lib
std
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();
+}