80 lines
2.3 KiB
Zig
80 lines
2.3 KiB
Zig
// ErrCtx is a way to pass friendly error messages to the user.
|
|
// Preallocates memory. Discards messages that do not fit.
|
|
|
|
const std = @import("std");
|
|
const mem = std.mem;
|
|
const math = std.math;
|
|
const BoundedArray = std.BoundedArray;
|
|
|
|
const capacity = 1 << 16; // 64K
|
|
const ErrCtx = @This();
|
|
|
|
buf: BoundedArray(u8, capacity) =
|
|
BoundedArray(u8, capacity).init(0) catch unreachable,
|
|
overflow: bool = false,
|
|
dirty: bool = false,
|
|
|
|
pub fn write(self: *ErrCtx, bytes: []const u8) error{}!usize {
|
|
self.dirty = true;
|
|
const can_add = capacity - self.buf.len;
|
|
if (can_add == 0) return bytes.len;
|
|
|
|
self.overflow = bytes.len > can_add;
|
|
self.buf.appendSliceAssumeCapacity(bytes[0..math.min(bytes.len, can_add)]);
|
|
// not adding the final zero is ok, because it needs
|
|
// to be ignored in the iterator anyway.
|
|
_ = self.buf.append(0) catch null;
|
|
|
|
return bytes.len;
|
|
}
|
|
|
|
const Writer = std.io.Writer(*ErrCtx, error{}, write);
|
|
|
|
pub fn writer(self: *ErrCtx) Writer {
|
|
return Writer{ .context = self };
|
|
}
|
|
|
|
pub fn err(self: *ErrCtx, comptime format: []const u8, args: anytype) void {
|
|
self.writer().print(format, args) catch unreachable;
|
|
}
|
|
|
|
pub fn iterator(self: *const ErrCtx) mem.SplitIterator(u8) {
|
|
const slice = self.buf.constSlice();
|
|
const last_byte = if (slice[slice.len - 1] == '0') slice.len - 1 else slice.len;
|
|
return mem.split(u8, slice[0..last_byte], "\x00");
|
|
}
|
|
|
|
const testing = std.testing;
|
|
|
|
test "basics" {
|
|
var ctx = ErrCtx{};
|
|
var wr = ctx.writer();
|
|
|
|
try wr.writeAll("0" ** 10);
|
|
try wr.writeAll("1" ** 10);
|
|
try wr.writeAll("3" ** capacity);
|
|
try wr.writeAll("foo");
|
|
|
|
var it = ctx.iterator();
|
|
try testing.expectEqualSlices(u8, it.next().?, "0" ** 10);
|
|
try testing.expectEqualSlices(u8, it.next().?, "1" ** 10);
|
|
|
|
const long = it.next().?;
|
|
try testing.expectEqual(@as(usize, capacity - 2 - 20), long.len);
|
|
try testing.expectEqual(it.next(), null);
|
|
try testing.expect(ctx.overflow);
|
|
}
|
|
|
|
test "almost overflow" {
|
|
var ctx = ErrCtx{};
|
|
var wr = ctx.writer();
|
|
|
|
try wr.writeAll("0" ** (capacity - 2));
|
|
try wr.writeAll("11");
|
|
|
|
var it = ctx.iterator();
|
|
try testing.expectEqualSlices(u8, it.next().?, "0" ** (capacity - 2));
|
|
try testing.expectEqualSlices(u8, it.next().?, "1");
|
|
try testing.expectEqual(it.next(), null);
|
|
}
|