summaryrefslogtreecommitdiff
path: root/clap
diff options
context:
space:
mode:
Diffstat (limited to 'clap')
-rw-r--r--clap/codepoint_counting_writer.zig68
-rw-r--r--clap/streaming.zig6
2 files changed, 39 insertions, 35 deletions
diff --git a/clap/codepoint_counting_writer.zig b/clap/codepoint_counting_writer.zig
index c445c90..3518d48 100644
--- a/clap/codepoint_counting_writer.zig
+++ b/clap/codepoint_counting_writer.zig
@@ -1,30 +1,39 @@
1/// A Writer that counts how many codepoints has been written to it. 1/// A Writer that counts how many codepoints has been written to it.
2/// Expects valid UTF-8 input, and does not validate the input. 2/// Expects valid UTF-8 input, and does not validate the input.
3pub fn CodepointCountingWriter(comptime WriterType: type) type { 3pub const CodepointCountingWriter = struct {
4 return struct { 4 codepoints_written: u64 = 0,
5 codepoints_written: u64, 5 child_stream: *std.Io.Writer,
6 child_stream: WriterType, 6 interface: std.Io.Writer = .{
7 7 .buffer = &.{},
8 pub const Error = WriterType.Error || error{Utf8InvalidStartByte}; 8 .vtable = &.{ .drain = drain },
9 pub const Writer = std.io.Writer(*Self, Error, write); 9 },
10
11 const Self = @This();
12
13 pub fn init(child_stream: *std.Io.Writer) Self {
14 return .{
15 .child_stream = child_stream,
16 };
17 }
10 18
11 const Self = @This(); 19 fn drain(w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
20 const self: *Self = @fieldParentPtr("interface", w);
21 var n_bytes_written: usize = 0;
22 var i: usize = 0;
12 23
13 pub fn write(self: *Self, bytes: []const u8) Error!usize { 24 while (i < data.len + splat - 1) : (i += 1) {
14 const bytes_and_codepoints = try utf8CountCodepointsAllowTruncate(bytes); 25 const chunk = data[@min(i, data.len)];
26 const bytes_and_codepoints = utf8CountCodepointsAllowTruncate(chunk) catch return std.Io.Writer.Error.WriteFailed;
15 // Might not be the full input, so the leftover bytes are written on the next call. 27 // Might not be the full input, so the leftover bytes are written on the next call.
16 const bytes_to_write = bytes[0..bytes_and_codepoints.bytes]; 28 const bytes_to_write = chunk[0..bytes_and_codepoints.bytes];
17 const amt = try self.child_stream.write(bytes_to_write); 29 const amt = try self.child_stream.write(bytes_to_write);
30 n_bytes_written += amt;
18 const bytes_written = bytes_to_write[0..amt]; 31 const bytes_written = bytes_to_write[0..amt];
19 self.codepoints_written += (try utf8CountCodepointsAllowTruncate(bytes_written)).codepoints; 32 self.codepoints_written += (utf8CountCodepointsAllowTruncate(bytes_written) catch return std.Io.Writer.Error.WriteFailed).codepoints;
20 return amt;
21 }
22
23 pub fn writer(self: *Self) Writer {
24 return .{ .context = self };
25 } 33 }
26 }; 34 return n_bytes_written;
27} 35 }
36};
28 37
29// Like `std.unicode.utf8CountCodepoints`, but on truncated input, it returns 38// Like `std.unicode.utf8CountCodepoints`, but on truncated input, it returns
30// the number of codepoints up to that point. 39// the number of codepoints up to that point.
@@ -58,44 +67,39 @@ fn utf8CountCodepointsAllowTruncate(s: []const u8) !struct { bytes: usize, codep
58 return .{ .bytes = i, .codepoints = len }; 67 return .{ .bytes = i, .codepoints = len };
59} 68}
60 69
61pub fn codepointCountingWriter(child_stream: anytype) CodepointCountingWriter(@TypeOf(child_stream)) {
62 return .{ .codepoints_written = 0, .child_stream = child_stream };
63}
64
65const testing = std.testing; 70const testing = std.testing;
66 71
67test CodepointCountingWriter { 72test CodepointCountingWriter {
68 var counting_stream = codepointCountingWriter(std.io.null_writer); 73 var discarding = std.Io.Writer.Discarding.init(&.{});
69 const stream = counting_stream.writer(); 74 var counting_stream = CodepointCountingWriter.init(&discarding.writer);
70 75
71 const utf8_text = "blåhaj" ** 100; 76 const utf8_text = "blåhaj" ** 100;
72 stream.writeAll(utf8_text) catch unreachable; 77 counting_stream.interface.writeAll(utf8_text) catch unreachable;
73 const expected_count = try std.unicode.utf8CountCodepoints(utf8_text); 78 const expected_count = try std.unicode.utf8CountCodepoints(utf8_text);
74 try testing.expectEqual(expected_count, counting_stream.codepoints_written); 79 try testing.expectEqual(expected_count, counting_stream.codepoints_written);
75} 80}
76 81
77test "handles partial UTF-8 writes" { 82test "handles partial UTF-8 writes" {
78 var buf: [100]u8 = undefined; 83 var buf: [100]u8 = undefined;
79 var fbs = std.io.fixedBufferStream(&buf); 84 var fbs = std.Io.Writer.fixed(&buf);
80 var counting_stream = codepointCountingWriter(fbs.writer()); 85 var counting_stream = CodepointCountingWriter.init(&fbs);
81 const stream = counting_stream.writer();
82 86
83 const utf8_text = "ååå"; 87 const utf8_text = "ååå";
84 // `å` is represented as `\xC5\xA5`, write 1.5 `å`s. 88 // `å` is represented as `\xC5\xA5`, write 1.5 `å`s.
85 var wc = try stream.write(utf8_text[0..3]); 89 var wc = try counting_stream.interface.write(utf8_text[0..3]);
86 // One should have been written fully. 90 // One should have been written fully.
87 try testing.expectEqual("å".len, wc); 91 try testing.expectEqual("å".len, wc);
88 try testing.expectEqual(1, counting_stream.codepoints_written); 92 try testing.expectEqual(1, counting_stream.codepoints_written);
89 93
90 // Write the rest, continuing from the reported number of bytes written. 94 // Write the rest, continuing from the reported number of bytes written.
91 wc = try stream.write(utf8_text[wc..]); 95 wc = try counting_stream.interface.write(utf8_text[wc..]);
92 try testing.expectEqual(4, wc); 96 try testing.expectEqual(4, wc);
93 try testing.expectEqual(3, counting_stream.codepoints_written); 97 try testing.expectEqual(3, counting_stream.codepoints_written);
94 98
95 const expected_count = try std.unicode.utf8CountCodepoints(utf8_text); 99 const expected_count = try std.unicode.utf8CountCodepoints(utf8_text);
96 try testing.expectEqual(expected_count, counting_stream.codepoints_written); 100 try testing.expectEqual(expected_count, counting_stream.codepoints_written);
97 101
98 try testing.expectEqualSlices(u8, utf8_text, fbs.getWritten()); 102 try testing.expectEqualSlices(u8, utf8_text, fbs.buffered());
99} 103}
100 104
101const std = @import("std"); 105const std = @import("std");
diff --git a/clap/streaming.zig b/clap/streaming.zig
index 4a687a2..fa5ec70 100644
--- a/clap/streaming.zig
+++ b/clap/streaming.zig
@@ -234,9 +234,9 @@ fn expectError(
234 234
235 while (parser.next() catch |err| { 235 while (parser.next() catch |err| {
236 var buf: [1024]u8 = undefined; 236 var buf: [1024]u8 = undefined;
237 var fbs = std.io.fixedBufferStream(&buf); 237 var fbs = std.Io.Writer.fixed(&buf);
238 diag.report(fbs.writer(), err) catch return error.TestFailed; 238 diag.report(&fbs, err) catch return error.TestFailed;
239 try std.testing.expectEqualStrings(expected, fbs.getWritten()); 239 try std.testing.expectEqualStrings(expected, fbs.buffered());
240 return; 240 return;
241 }) |_| {} 241 }) |_| {}
242 242