diff options
Diffstat (limited to 'clap')
| -rw-r--r-- | clap/codepoint_counting_writer.zig | 68 | ||||
| -rw-r--r-- | clap/streaming.zig | 6 |
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. |
| 3 | pub fn CodepointCountingWriter(comptime WriterType: type) type { | 3 | pub 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 | ||
| 61 | pub fn codepointCountingWriter(child_stream: anytype) CodepointCountingWriter(@TypeOf(child_stream)) { | ||
| 62 | return .{ .codepoints_written = 0, .child_stream = child_stream }; | ||
| 63 | } | ||
| 64 | |||
| 65 | const testing = std.testing; | 70 | const testing = std.testing; |
| 66 | 71 | ||
| 67 | test CodepointCountingWriter { | 72 | test 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 | ||
| 77 | test "handles partial UTF-8 writes" { | 82 | test "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 | ||
| 101 | const std = @import("std"); | 105 | const 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 | ||