diff --git a/bench/src/reader.zig b/bench/src/reader.zig index 34d60d1..8a82fbb 100644 --- a/bench/src/reader.zig +++ b/bench/src/reader.zig @@ -5,7 +5,9 @@ pub const main = @import("common.zig").main; pub fn runBench(data: []const u8) !void { var data_stream = std.io.fixedBufferStream(data); - var reader = xml.reader(std.heap.c_allocator, data_stream.reader(), xml.encoding.Utf8Decoder{}, .{}); + var reader = xml.reader(std.heap.c_allocator, data_stream.reader(), .{ + .DecoderType = xml.encoding.Utf8Decoder, + }); defer reader.deinit(); while (try reader.next()) |_| {} } diff --git a/bench/src/token_reader.zig b/bench/src/token_reader.zig index 13868ed..2190008 100644 --- a/bench/src/token_reader.zig +++ b/bench/src/token_reader.zig @@ -5,6 +5,8 @@ pub const main = @import("common.zig").main; pub fn runBench(data: []const u8) !void { var data_stream = std.io.fixedBufferStream(data); - var token_reader = xml.tokenReader(data_stream.reader(), xml.encoding.Utf8Decoder{}, .{}); + var token_reader = xml.tokenReader(data_stream.reader(), .{ + .DecoderType = xml.encoding.Utf8Decoder, + }); while (try token_reader.next()) |_| {} } diff --git a/examples/read.zig b/examples/read.zig index 7102a50..3fb77f3 100644 --- a/examples/read.zig +++ b/examples/read.zig @@ -20,7 +20,7 @@ pub fn main() !void { const input_file = try std.fs.cwd().openFile(input_path, .{}); defer input_file.close(); var input_buffered_reader = std.io.bufferedReader(input_file.reader()); - var reader = xml.reader(allocator, input_buffered_reader.reader(), xml.encoding.DefaultDecoder{}, .{}); + var reader = xml.reader(allocator, input_buffered_reader.reader(), .{}); defer reader.deinit(); while (try reader.next()) |event| { diff --git a/src/reader.zig b/src/reader.zig index 5906b06..4eb7f77 100644 --- a/src/reader.zig +++ b/src/reader.zig @@ -294,10 +294,9 @@ pub const NoOpNamespaceContext = struct { pub fn reader( allocator: Allocator, r: anytype, - decoder: anytype, comptime options: ReaderOptions, -) Reader(@TypeOf(r), @TypeOf(decoder), options) { - return Reader(@TypeOf(r), @TypeOf(decoder), options).init(allocator, r, decoder); +) Reader(@TypeOf(r), options) { + return Reader(@TypeOf(r), options).init(allocator, r, .{}); } /// Reads a full XML document from a `std.io.Reader`. @@ -354,6 +353,8 @@ pub fn readDocument( /// Options for a `Reader`. pub const ReaderOptions = struct { + /// The type of decoder to use. + DecoderType: type = encoding.DefaultDecoder, /// The size of the internal buffer. /// /// This limits the byte length of "non-splittable" content, such as @@ -390,11 +391,7 @@ pub const ReaderOptions = struct { /// Since this parser wraps a `TokenReader`, the caveats on the `buffer_size` /// bounding the length of "non-splittable" content which are outlined in its /// documentation apply here as well. -pub fn Reader( - comptime ReaderType: type, - comptime DecoderType: type, - comptime options: ReaderOptions, -) type { +pub fn Reader(comptime ReaderType: type, comptime options: ReaderOptions) type { return struct { token_reader: TokenReaderType, /// A stack of element names enclosing the current context. @@ -422,7 +419,8 @@ pub fn Reader( allocator: Allocator, const Self = @This(); - const TokenReaderType = TokenReader(ReaderType, DecoderType, .{ + const TokenReaderType = TokenReader(ReaderType, .{ + .DecoderType = options.DecoderType, .buffer_size = options.buffer_size, .enable_normalization = options.enable_normalization, .track_location = options.track_location, @@ -439,7 +437,7 @@ pub fn Reader( QNameNotAllowed, } || Allocator.Error || TokenReaderType.Error; - pub fn init(allocator: Allocator, r: ReaderType, decoder: DecoderType) Self { + pub fn init(allocator: Allocator, r: ReaderType, decoder: options.DecoderType) Self { return .{ .token_reader = TokenReaderType.init(r, decoder), .event_arena = ArenaAllocator.init(allocator), @@ -916,7 +914,7 @@ test "namespace handling" { fn testValid(comptime options: ReaderOptions, input: []const u8, expected_events: []const Event) !void { var input_stream = std.io.fixedBufferStream(input); - var input_reader = reader(testing.allocator, input_stream.reader(), encoding.Utf8Decoder{}, options); + var input_reader = reader(testing.allocator, input_stream.reader(), options); defer input_reader.deinit(); var i: usize = 0; while (try input_reader.next()) |event| : (i += 1) { @@ -937,7 +935,7 @@ fn testValid(comptime options: ReaderOptions, input: []const u8, expected_events fn testInvalid(comptime options: ReaderOptions, input: []const u8, expected_error: anyerror) !void { var input_stream = std.io.fixedBufferStream(input); - var input_reader = reader(testing.allocator, input_stream.reader(), encoding.Utf8Decoder{}, options); + var input_reader = reader(testing.allocator, input_stream.reader(), options); defer input_reader.deinit(); while (input_reader.next()) |_| {} else |err| { try testing.expectEqual(expected_error, err); @@ -966,7 +964,7 @@ test "nextNode" { \\ \\ ); - var input_reader = reader(testing.allocator, input_stream.reader(), encoding.Utf8Decoder{}, .{}); + var input_reader = reader(testing.allocator, input_stream.reader(), .{}); defer input_reader.deinit(); try testing.expectEqualDeep(@as(?Event, .{ .xml_declaration = .{ .version = "1.0" } }), try input_reader.next()); @@ -1015,7 +1013,7 @@ test "nextNode namespace handling" { \\ \\ ); - var input_reader = reader(testing.allocator, input_stream.reader(), encoding.Utf8Decoder{}, .{}); + var input_reader = reader(testing.allocator, input_stream.reader(), .{}); defer input_reader.deinit(); var root_start = try input_reader.next(); @@ -1065,7 +1063,7 @@ test readDocument { \\ \\ ); - var document_node = try readDocument(testing.allocator, input_stream.reader(), encoding.Utf8Decoder{}, .{}); + var document_node = try readDocument(testing.allocator, input_stream.reader(), .{}); defer document_node.deinit(); try testing.expectEqualDeep(Node.Document{ .version = "1.0", .children = &.{ @@ -1103,7 +1101,7 @@ test Children { \\ \\ ); - var input_reader = reader(testing.allocator, input_stream.reader(), encoding.Utf8Decoder{}, .{}); + var input_reader = reader(testing.allocator, input_stream.reader(), .{}); defer input_reader.deinit(); try testing.expectEqualDeep(@as(?Event, .{ .element_start = .{ .name = .{ .local = "root" } } }), try input_reader.next()); @@ -1135,7 +1133,7 @@ test "skip children" { \\ \\ ); - var input_reader = reader(testing.allocator, input_stream.reader(), encoding.Utf8Decoder{}, .{}); + var input_reader = reader(testing.allocator, input_stream.reader(), .{}); defer input_reader.deinit(); try testing.expectEqualDeep(@as(?Event, .{ .element_start = .{ .name = .{ .local = "root" } } }), try input_reader.next()); diff --git a/src/token_reader.zig b/src/token_reader.zig index d3601c6..25cda3f 100644 --- a/src/token_reader.zig +++ b/src/token_reader.zig @@ -158,14 +158,15 @@ pub const NoOpLocation = struct { /// (4096). pub fn tokenReader( reader: anytype, - decoder: anytype, comptime options: TokenReaderOptions, -) TokenReader(@TypeOf(reader), @TypeOf(decoder), options) { - return TokenReader(@TypeOf(reader), @TypeOf(decoder), options).init(reader, decoder); +) TokenReader(@TypeOf(reader), options) { + return TokenReader(@TypeOf(reader), options).init(reader, .{}); } /// Options for a `TokenReader`. pub const TokenReaderOptions = struct { + /// The type of decoder to use. + DecoderType: type = encoding.DefaultDecoder, /// The size of the internal buffer. /// /// This limits the byte length of "non-splittable" content, such as @@ -204,15 +205,11 @@ pub const TokenReaderOptions = struct { /// important. Additionally, `buffer_size` limits the maximum byte length of /// "unsplittable" content, such as element and attribute names (but not /// "splittable" content, such as element text content and attribute values). -pub fn TokenReader( - comptime ReaderType: type, - comptime DecoderType: type, - comptime options: TokenReaderOptions, -) type { +pub fn TokenReader(comptime ReaderType: type, comptime options: TokenReaderOptions) type { return struct { scanner: Scanner, reader: ReaderType, - decoder: DecoderType, + decoder: options.DecoderType, /// The current location in the file (if enabled). location: if (options.track_location) Location else NoOpLocation = .{}, /// Buffered content read by the reader for the current token. @@ -232,11 +229,11 @@ pub fn TokenReader( InvalidPiTarget, Overflow, UnexpectedEndOfInput, - } || ReaderType.Error || DecoderType.Error || Scanner.Error; + } || ReaderType.Error || options.DecoderType.Error || Scanner.Error; - const max_encoded_codepoint_len = @max(DecoderType.max_encoded_codepoint_len, 4); + const max_encoded_codepoint_len = @max(options.DecoderType.max_encoded_codepoint_len, 4); - pub fn init(reader: ReaderType, decoder: DecoderType) Self { + pub fn init(reader: ReaderType, decoder: options.DecoderType) Self { return .{ .scanner = Scanner{}, .reader = reader, @@ -510,7 +507,7 @@ test "PI target" { fn testValid(comptime options: TokenReaderOptions, input: []const u8, expected_tokens: []const Token) !void { var input_stream = std.io.fixedBufferStream(input); - var input_reader = tokenReader(input_stream.reader(), encoding.Utf8Decoder{}, options); + var input_reader = tokenReader(input_stream.reader(), options); var i: usize = 0; while (try input_reader.next()) |token| : (i += 1) { if (i >= expected_tokens.len) { @@ -530,7 +527,7 @@ fn testValid(comptime options: TokenReaderOptions, input: []const u8, expected_t fn testInvalid(comptime options: TokenReaderOptions, input: []const u8, expected_error: anyerror) !void { var input_stream = std.io.fixedBufferStream(input); - var input_reader = tokenReader(input_stream.reader(), encoding.Utf8Decoder{}, options); + var input_reader = tokenReader(input_stream.reader(), options); while (input_reader.next()) |_| {} else |err| { try testing.expectEqual(expected_error, err); }