From b101e0acd2e1f9b3e9e87b0ba028bb1bb34f9b17 Mon Sep 17 00:00:00 2001 From: torque Date: Wed, 23 Aug 2023 22:03:25 -0700 Subject: [PATCH] message: more iterators, less problems This seems like a pretty nice way to do this, since it lets us produce the sliced version of each value rather than the somewhat janky many pointer. --- examples/headers.zig | 31 ++++++++++++++++++------------- src/message.zig | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/examples/headers.zig b/examples/headers.zig index 4846b11..f30972c 100644 --- a/examples/headers.zig +++ b/examples/headers.zig @@ -15,14 +15,20 @@ pub fn main() !void { try message.deleteHeader("My-Key3"); - var iter = try message.headerIterator(); - defer iter.destroy(); + { + var iter = try message.headerIterator(); + defer iter.destroy(); - while (try iter.next()) |pair| { - std.debug.print( - "Key: '{s}', Value: '{s}'\n", - .{ pair.key, pair.value orelse "null" }, - ); + while (iter.next()) |resolv| { + var val_iter = try resolv.getValueIterator(); + defer val_iter.destroy(); + + std.debug.print("key '{s}' got: ", .{resolv.key}); + while (val_iter.next()) |value| { + std.debug.print("'{s}', ", .{value}); + } + std.debug.print("\n", .{}); + } } const subscription = try connection.subscribeSync("subject"); @@ -33,13 +39,12 @@ pub fn main() !void { defer received.destroy(); { - const vals1 = try received.getAllHeaderValues("My-Key1"); - defer std.heap.raw_c_allocator.free(vals1); - std.debug.print("For key 'My-Key1' got: ", .{}); - for (vals1) |value| { - const val = std.mem.sliceTo(value, 0); + var iter = try received.getHeaderValueIterator("My-Key1"); + defer iter.destroy(); - std.debug.print("'{s}', ", .{val}); + std.debug.print("For key 'My-Key1' got: ", .{}); + while (iter.next()) |value| { + std.debug.print("'{s}', ", .{value}); } std.debug.print("\n", .{}); } diff --git a/src/message.zig b/src/message.zig index 03eb471..c604745 100644 --- a/src/message.zig +++ b/src/message.zig @@ -76,6 +76,10 @@ pub const Message = opaque { }; } + pub fn getHeaderValueIterator(self: *Message, key: [:0]const u8) Error!HeaderValueIterator { + return .{ .values = try self.getAllHeaderValues(key) }; + } + pub fn getAllHeaderKeys(self: *Message) Error![][*:0]const u8 { var keys: [*c][*c]const u8 = undefined; var count: c_int = 0; @@ -93,23 +97,56 @@ pub const Message = opaque { }; } + pub const HeaderValueIterator = struct { + values: [][*:0]const u8, + index: usize = 0, + + pub fn destroy(self: HeaderValueIterator) void { + std.heap.raw_c_allocator.free(self.values); + } + + pub fn next(self: *HeaderValueIterator) ?[:0]const u8 { + if (self.index >= self.values.len) return null; + defer self.index += 1; + + return std.mem.sliceTo(self.values[self.index], 0); + } + }; + pub const HeaderIterator = struct { message: *Message, keys: [][*:0]const u8, index: usize = 0, + pub const ValueResolver = struct { + message: *Message, + key: [:0]const u8, + + pub fn getValue(self: ValueResolver) Error![:0]const u8 { + // TODO: if we didn't care about the lifecycle of self.message, we + // could do catch unreachable here and make this error-free + return try self.message.getHeaderValue(self.key); + } + + pub fn getValueIterator(self: ValueResolver) Error!HeaderValueIterator { + return .{ + .values = try self.message.getAllHeaderValues(self.key), + }; + } + }; + pub fn destroy(self: *HeaderIterator) void { std.heap.raw_c_allocator.free(self.keys); } - pub fn next(self: *HeaderIterator) Error!?struct { key: [:0]const u8, value: ?[:0]const u8 } { + pub fn next(self: *HeaderIterator) ?ValueResolver { if (self.index >= self.keys.len) return null; defer self.index += 1; const sliced = std.mem.sliceTo(self.keys[self.index], 0); return .{ + .message = self.message, .key = sliced, - .value = try self.message.getHeaderValue(sliced), }; }