108 lines
3.0 KiB
Zig
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();
|
|
}
|