lib/pretty/core/src/compact/cap.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const footer_dash = "\xE2\x80\x94";
4 pub const truncation_line = "(output truncated at 16 KB " ++ footer_dash ++
5 " use filters or --limit to scope down, --json for exact records)";
6 pub const full_cap = 16 * 1024;
7 const reserved = truncation_line.len + 1;
8 pub const body_budget = full_cap - reserved;
9
10 pub fn cap16kb(allocator: std.mem.Allocator, output: []const u8) ![]u8 {
11 if (output.len <= body_budget) return try allocator.dupe(u8, output);
12 var end: usize = 0;
13 var next: usize = 0;
14 while (next < output.len and next < body_budget) {
15 const line_end = std.mem.indexOfScalarPos(u8, output, next, '\n') orelse break;
16 if (line_end + 1 > body_budget) break;
17 end = line_end + 1;
18 next = line_end + 1;
19 }
20 var out = std.Io.Writer.Allocating.init(allocator);
21 errdefer out.deinit();
22 if (end != 0) try out.writer.writeAll(output[0..end]);
23 try out.writer.writeAll(truncation_line);
24 try out.writer.writeByte('\n');
25 return try out.toOwnedSlice();
26 }
27
28 test "output cap keeps line boundary" {
29 const allocator = std.testing.allocator;
30 var source: std.ArrayList(u8) = .empty;
31 defer source.deinit(allocator);
32 for (0..4000) |_| try source.appendSlice(allocator, "line\n");
33 const out = try cap16kb(allocator, source.items);
34 defer allocator.free(out);
35 try std.testing.expect(std.mem.endsWith(u8, out, truncation_line ++ "\n"));
36 }