From 6dc50530f1112f99690195f8586caaace5c85704 Mon Sep 17 00:00:00 2001 From: torque Date: Sun, 3 Sep 2023 16:13:58 -0700 Subject: [PATCH] message: a bit of cleanup around header iterators --- examples/headers.zig | 27 +++--- src/message.zig | 199 +++++++++++++++++++++++-------------------- 2 files changed, 121 insertions(+), 105 deletions(-) diff --git a/examples/headers.zig b/examples/headers.zig index c86f5a9..3b09200 100644 --- a/examples/headers.zig +++ b/examples/headers.zig @@ -11,24 +11,25 @@ pub fn main() !void { const message = try nats.Message.create("subject", null, "message"); defer message.destroy(); - try message.setHeaderValue("My-Key1", "value1"); - try message.setHeaderValue("My-Key2", "value2"); - try message.addHeaderValue("My-Key1", "value3"); - try message.setHeaderValue("My-Key3", "value4"); + try message.setHeaderValue("foo", "foo-value"); + try message.setHeaderValue("bar", "bar-value"); + try message.addHeaderValue("foo", "bar-value"); + try message.setHeaderValue("baz", "baz-value"); + try message.addHeaderValue("qux", "qux-value"); - try message.deleteHeader("My-Key3"); + try message.deleteHeader("baz"); { - var iter = try message.headerIterator(); + var iter = try message.getHeaderIterator(); defer iter.destroy(); - while (iter.next()) |resolv| { - var val_iter = try resolv.getValueIterator(); + while (iter.next()) |header| { + var val_iter = try header.valueIterator(); defer val_iter.destroy(); - std.debug.print("key '{s}' got: ", .{resolv.key}); + std.debug.print("key '{s}' got: ", .{header.key}); while (val_iter.next()) |value| { - std.debug.print("'{s}', ", .{value}); + std.debug.print("'{s}'{s}", .{ value, if (val_iter.peek()) |_| ", " else "" }); } std.debug.print("\n", .{}); } @@ -42,12 +43,12 @@ pub fn main() !void { defer received.destroy(); { - var iter = try received.getHeaderValueIterator("My-Key1"); + var iter = try received.getHeaderValueIterator("foo"); defer iter.destroy(); - std.debug.print("For key 'My-Key1' got: ", .{}); + std.debug.print("For key 'foo' got: ", .{}); while (iter.next()) |value| { - std.debug.print("'{s}', ", .{value}); + std.debug.print("'{s}'{s}", .{ value, if (iter.peek()) |_| ", " else "" }); } std.debug.print("\n", .{}); } diff --git a/src/message.zig b/src/message.zig index c7a3f79..08e1dd5 100644 --- a/src/message.zig +++ b/src/message.zig @@ -76,102 +76,11 @@ pub const Message = opaque { return status.toError() orelse std.mem.sliceTo(value.?, 0); } - pub fn getAllHeaderValues(self: *Message, key: [:0]const u8) Error![][*:0]const u8 { - var values: [*c][*c]const u8 = undefined; - var count: c_int = 0; - - const status = Status.fromInt(nats_c.natsMsgHeader_Values(@ptrCast(self), key.ptr, &values, &count)); - - // the user must use std.mem.spanTo on each item they want to read to get a - // slice, since we can't do that automatically without having to allocate. - return status.toError() orelse blk: { - const coerced: [*][*:0]const u8 = @ptrFromInt(@intFromPtr(values)); - break :blk coerced[0..@intCast(count)]; - }; - } - 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; - - const status = Status.fromInt(nats_c.natsMsgHeader_Keys(@ptrCast(self), &keys, &count)); - - // TODO: manually assert no keys are NULL? - - // the user must use std.mem.spanTo on each item they want to read to get a - // slice, since we can't do that automatically without having to allocate. - // the returned slice - return status.toError() orelse blk: { - const coerced: [*][*:0]const u8 = @ptrFromInt(@intFromPtr(keys)); - break :blk coerced[0..@intCast(count)]; - }; - } - - 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) ?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, - }; - } - - pub fn nextKey(self: *HeaderIterator) ?[:0]const u8 { - if (self.index >= self.keys.len) return null; - defer self.index += 1; - return std.mem.sliceTo(self.keys[self.index], 0); - } - }; - - pub fn headerIterator(self: *Message) Error!HeaderIterator { + pub fn getHeaderIterator(self: *Message) Error!HeaderIterator { return .{ .message = self, .keys = try self.getAllHeaderKeys(), @@ -186,6 +95,112 @@ pub const Message = opaque { pub fn isNoResponders(self: *Message) bool { return nats_c.natsMsg_IsNoResponders(@ptrCast(self)); } + + // prefer using message.getHeaderValueIterator + pub fn getAllHeaderValues(self: *Message, key: [:0]const u8) Error![][*:0]const u8 { + var values: [*][*:0]const u8 = undefined; + var count: c_int = 0; + + const status = Status.fromInt( + nats_c.natsMsgHeader_Values(@ptrCast(self), key.ptr, @ptrCast(&values), &count), + ); + + // the user must use std.mem.spanTo on each item they want to read to get a + // slice, since we can't do that automatically without having to allocate. + return status.toError() orelse values[0..@intCast(count)]; + } + + // prefer using message.getHeaderIterator + pub fn getAllHeaderKeys(self: *Message) Error![][*:0]const u8 { + var keys: [*][*:0]const u8 = undefined; + var count: c_int = 0; + + const status = Status.fromInt(nats_c.natsMsgHeader_Keys(@ptrCast(self), @ptrCast(&keys), &count)); + + // the user must use std.mem.spanTo on each item they want to read to get a + // slice, since we can't do that automatically without having to allocate. + // the returned slice + return status.toError() orelse keys[0..@intCast(count)]; + } + + 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 const deinit = HeaderValueIterator.destroy; + + 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 fn peek(self: *HeaderValueIterator) ?[:0]const u8 { + if (self.index >= self.values.len) return null; + 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 value(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 valueIterator(self: ValueResolver) Error!HeaderValueIterator { + return try self.message.getHeaderValueIterator(self.key); + } + }; + + pub fn destroy(self: *HeaderIterator) void { + std.heap.raw_c_allocator.free(self.keys); + } + + pub const deinit = HeaderIterator.destroy; + + pub fn next(self: *HeaderIterator) ?ValueResolver { + if (self.index >= self.keys.len) return null; + defer self.index += 1; + + return .{ + .message = self.message, + .key = std.mem.sliceTo(self.keys[self.index], 0), + }; + } + + pub fn peek(self: *HeaderIterator) ?ValueResolver { + if (self.index >= self.keys.len) return null; + return .{ + .message = self.message, + .key = std.mem.sliceTo(self.keys[self.index], 0), + }; + } + + pub fn nextKey(self: *HeaderIterator) ?[:0]const u8 { + if (self.index >= self.keys.len) return null; + defer self.index += 1; + return std.mem.sliceTo(self.keys[self.index], 0); + } + + pub fn peekKey(self: *HeaderIterator) ?[:0]const u8 { + if (self.index >= self.keys.len) return null; + return std.mem.sliceTo(self.keys[self.index], 0); + } + }; }; // TODO: not implementing jetstream API right now