zig/lib/std/progress.zig
2019-10-17 13:32:45 -04:00

108 lines
3.0 KiB
Zig

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();
}