authorgravatar for git@l4.pmLuna <git@l4.pm> 2019-10-02 16:30:12-03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-17 13:32:45-04:00
loga73c7bcaf997fddd3aa746104e930cef8b08a934
treea95bee6a07474f1ca31bcf7cd4a04871fd05e688
parent17aa8c3ee29fae4bfbd6acc91eed8d229f627c32
signaturelock-open Commit is signed but in an unrecognized format.

add lib/std/progress.zig


1 files changed, 107 insertions(+), 0 deletions(-)

lib/std/progress.zig created+107
...@@ -0,0 +1,107 @@
1const std = @import("std");
2const testing = std.testing;
3
4pub const PrintConfig = struct {
5 /// If the current node (and its children) should
6 /// print to stderr on update()
7 flag: bool = false,
8
9 /// If all output should be suppressed instead
10 /// serves the same practical purpose as `flag` but supposed to be used
11 /// by separate parts of the user program.
12 suppress: bool = false,
13};
14
15pub const ProgressNode = struct {
16 completed_items: usize = 0,
17 total_items: usize,
18
19 print_config: PrintConfig,
20
21 // TODO maybe instead of keeping a prefix field, we could
22 // select the proper prefix at the time of update(), and if we're not
23 // in a terminal, we use warn("/r{}", lots_of_whitespace).
24 prefix: []const u8,
25
26 /// Create a new progress node.
27 pub fn start(
28 parent_opt: ?ProgressNode,
29 total_items_opt: ?usize,
30 ) !ProgressNode {
31
32 // inherit the last set print "configuration" from the parent node
33 var print_config = PrintConfig{};
34 if (parent_opt) |parent| {
35 print_config = parent.print_config;
36 }
37
38 var stderr = try std.io.getStdErr();
39 const is_term = std.os.isatty(stderr.handle);
40
41 // if we're in a terminal, use vt100 escape codes
42 // for the progress.
43 var prefix: []const u8 = undefined;
44 if (is_term) {
45 prefix = "\x21[2K\r";
46 } else {
47 prefix = "\n";
48 }
49
50 return ProgressNode{
51 .total_items = total_items_opt orelse 0,
52 .print_config = print_config,
53 .prefix = prefix,
54 };
55 }
56
57 /// Signal an update on the progress node.
58 /// The user of this function is supposed to modify
59 /// ProgressNode.PrintConfig.flag when update() is supposed to print.
60 pub fn update(
61 self: *ProgressNode,
62 current_action: ?[]const u8,
63 items_done_opt: ?usize,
64 ) void {
65 if (items_done_opt) |items_done| {
66 self.completed_items = items_done;
67
68 if (items_done > self.total_items) {
69 self.total_items = items_done;
70 }
71 }
72
73 var cfg = self.print_config;
74 if (cfg.flag and !cfg.suppress and current_action != null) {
75 std.debug.warn(
76 "{}[{}/{}] {}",
77 self.prefix,
78 self.completed_items,
79 self.total_items,
80 current_action,
81 );
82 }
83 }
84
85 pub fn end(self: *ProgressNode) void {
86 if (!self.print_config.flag) return;
87
88 // TODO emoji?
89 std.debug.warn("\n[V] done!");
90 }
91};
92
93test "basic functionality" {
94 var node = try ProgressNode.start(null, 100);
95
96 var buf: [100]u8 = undefined;
97
98 var i: usize = 0;
99 while (i < 100) : (i += 6) {
100 if (i > 50) node.print_config.flag = true;
101 const msg = try std.fmt.bufPrint(buf[0..], "action at i={}", i);
102 node.update(msg, i);
103 std.time.sleep(10 * std.time.millisecond);
104 }
105
106 node.end();
107}