thunk: rework handling of userdata

A number of cases were not handled properly: even though optional
userdata was allowed, the handling would cause unreachable to be
reached if null handles were actually passed. Also, being able to use
void to specify "no userdata please" is useful; however, in this case
we do have to still pass NULL to the C library calls.

The casting logic has been pulled out to some helper functions, which
make it more consistent. Some mediocre additional test coverage has
been added as well.
This commit is contained in:
2024-10-01 23:17:20 -07:00
parent 68a98232a1
commit c82911682e
6 changed files with 282 additions and 184 deletions

View File

@@ -99,42 +99,46 @@ test "nats.Connection" {
connection.drainTimeout(1000) catch {};
}
fn reconnectDelayHandler(userdata: *const u32, connection: *nats.Connection, attempts: c_int) i64 {
_ = userdata;
_ = connection;
_ = attempts;
fn callbacks(comptime UDT: type) type {
return struct {
fn reconnectDelayHandler(userdata: UDT, connection: *nats.Connection, attempts: c_int) i64 {
_ = userdata;
_ = connection;
_ = attempts;
return 0;
}
return 0;
}
fn errorHandler(
userdata: *const u32,
connection: *nats.Connection,
subscription: *nats.Subscription,
status: nats.Status,
) void {
_ = userdata;
_ = connection;
_ = subscription;
_ = status;
}
fn errorHandler(
userdata: UDT,
connection: *nats.Connection,
subscription: *nats.Subscription,
status: nats.Status,
) void {
_ = userdata;
_ = connection;
_ = subscription;
_ = status;
}
fn connectionHandler(userdata: *const u32, connection: *nats.Connection) void {
_ = userdata;
_ = connection;
}
fn connectionHandler(userdata: UDT, connection: *nats.Connection) void {
_ = userdata;
_ = connection;
}
fn jwtHandler(userdata: *const u32) nats.JwtResponseOrError {
_ = userdata;
// return .{ .jwt = std.heap.raw_c_allocator.dupeZ(u8, "abcdef") catch @panic("no!") };
return .{ .error_message = std.heap.raw_c_allocator.dupeZ(u8, "dang") catch @panic("no!") };
}
fn jwtHandler(userdata: UDT) nats.JwtResponseOrError {
_ = userdata;
// return .{ .jwt = std.heap.raw_c_allocator.dupeZ(u8, "abcdef") catch @panic("no!") };
return .{ .error_message = std.heap.raw_c_allocator.dupeZ(u8, "dang") catch @panic("no!") };
}
fn signatureHandler(userdata: *const u32, nonce: [:0]const u8) nats.SignatureResponseOrError {
_ = userdata;
_ = nonce;
// return .{ .signature = std.heap.raw_c_allocator.dupe(u8, "01230123") catch @panic("no!") };
return .{ .error_message = std.heap.raw_c_allocator.dupeZ(u8, "whoops") catch @panic("no!") };
fn signatureHandler(userdata: UDT, nonce: [:0]const u8) nats.SignatureResponseOrError {
_ = userdata;
_ = nonce;
// return .{ .signature = std.heap.raw_c_allocator.dupe(u8, "01230123") catch @panic("no!") };
return .{ .error_message = std.heap.raw_c_allocator.dupeZ(u8, "whoops") catch @panic("no!") };
}
};
}
test "nats.ConnectionOptions" {
@@ -164,14 +168,26 @@ test "nats.ConnectionOptions" {
try options.setMaxReconnect(10);
try options.setReconnectWait(500);
try options.setReconnectJitter(100, 200);
try options.setCustomReconnectDelay(*const u32, reconnectDelayHandler, &userdata);
try options.setCustomReconnectDelay(*const u32, callbacks(*const u32).reconnectDelayHandler, &userdata);
try options.setCustomReconnectDelay(void, callbacks(void).reconnectDelayHandler, {});
try options.setCustomReconnectDelay(?*const u32, callbacks(?*const u32).reconnectDelayHandler, null);
try options.setReconnectBufSize(1024);
try options.setMaxPendingMessages(50);
try options.setErrorHandler(*const u32, errorHandler, &userdata);
try options.setClosedCallback(*const u32, connectionHandler, &userdata);
try options.setDisconnectedCallback(*const u32, connectionHandler, &userdata);
try options.setDiscoveredServersCallback(*const u32, connectionHandler, &userdata);
try options.setLameDuckModeCallback(*const u32, connectionHandler, &userdata);
try options.setErrorHandler(*const u32, callbacks(*const u32).errorHandler, &userdata);
try options.setErrorHandler(void, callbacks(void).errorHandler, {});
try options.setErrorHandler(?*const u32, callbacks(?*const u32).errorHandler, null);
try options.setClosedCallback(*const u32, callbacks(*const u32).connectionHandler, &userdata);
try options.setClosedCallback(void, callbacks(void).connectionHandler, {});
try options.setClosedCallback(?*const u32, callbacks(?*const u32).connectionHandler, null);
try options.setDisconnectedCallback(*const u32, callbacks(*const u32).connectionHandler, &userdata);
try options.setDisconnectedCallback(void, callbacks(void).connectionHandler, {});
try options.setDisconnectedCallback(?*const u32, callbacks(?*const u32).connectionHandler, null);
try options.setDiscoveredServersCallback(*const u32, callbacks(*const u32).connectionHandler, &userdata);
try options.setDiscoveredServersCallback(void, callbacks(void).connectionHandler, {});
try options.setDiscoveredServersCallback(?*const u32, callbacks(?*const u32).connectionHandler, null);
try options.setLameDuckModeCallback(*const u32, callbacks(*const u32).connectionHandler, &userdata);
try options.setLameDuckModeCallback(void, callbacks(void).connectionHandler, {});
try options.setLameDuckModeCallback(?*const u32, callbacks(?*const u32).connectionHandler, null);
try options.ignoreDiscoveredServers(true);
try options.useGlobalMessageDelivery(false);
try options.ipResolutionOrder(.ipv4_first);
@@ -179,8 +195,11 @@ test "nats.ConnectionOptions" {
try options.useOldRequestStyle(false);
try options.setFailRequestsOnDisconnect(true);
try options.setNoEcho(true);
try options.setRetryOnFailedConnect(*const u32, connectionHandler, true, &userdata);
try options.setUserCredentialsCallbacks(*const u32, *const u32, jwtHandler, signatureHandler, &userdata, &userdata);
try options.setRetryOnFailedConnect(*const u32, callbacks(*const u32).connectionHandler, true, &userdata);
try options.setRetryOnFailedConnect(void, callbacks(void).connectionHandler, true, {});
try options.setRetryOnFailedConnect(?*const u32, callbacks(?*const u32).connectionHandler, true, null);
try options.setUserCredentialsCallbacks(*const u32, *const u32, callbacks(*const u32).jwtHandler, callbacks(*const u32).signatureHandler, &userdata, &userdata);
try options.setUserCredentialsCallbacks(void, void, callbacks(void).jwtHandler, callbacks(void).signatureHandler, {}, {});
try options.setWriteDeadline(5);
try options.disableNoResponders(true);
try options.setCustomInboxPrefix("_FOOBOX");

View File

@@ -1,9 +1,11 @@
// This file is licensed under the CC0 1.0 license.
// See: https://creativecommons.org/publicdomain/zero/1.0/legalcode
test {
_ = @import("./nats.zig");
_ = @import("./connection.zig");
_ = @import("./message.zig");
_ = @import("./subscription.zig");
comptime {
if (@import("builtin").is_test) {
_ = @import("./nats.zig");
_ = @import("./connection.zig");
_ = @import("./message.zig");
_ = @import("./subscription.zig");
}
}

View File

@@ -190,3 +190,109 @@ test "nats.Subscription (async)" {
);
}
}
fn onVoidMessage(
userdata: void,
connection: *nats.Connection,
subscription: *nats.Subscription,
message: *nats.Message,
) void {
_ = subscription;
_ = userdata;
if (message.getReply()) |reply| {
connection.publish(reply, "greetings") catch @panic("OH NO");
} else @panic("HOW");
}
fn onVoidClose(userdata: void) void {
_ = userdata;
}
test "nats.Subscription (async, void)" {
var server = try util.TestServer.launch(.{});
defer server.stop();
try nats.init(nats.default_spin_count);
defer nats.deinit();
const connection = try nats.Connection.connectTo(server.url);
defer connection.destroy();
const message_subject: [:0]const u8 = "hello";
const message_reply: [:0]const u8 = "reply";
const message_data: [:0]const u8 = "world";
const message = try nats.Message.create(message_subject, message_reply, message_data);
defer message.destroy();
{
{
const subscription = try connection.subscribe(void, message_subject, onVoidMessage, {});
defer subscription.destroy();
try subscription.setCompletionCallback(void, onVoidClose, {});
const response = try connection.requestMessage(message, 1000);
try std.testing.expectEqualStrings(
"greetings",
response.getData() orelse return error.TestUnexpectedResult,
);
}
// we have to sleep to allow the close callback to run.
nats.sleep(1);
}
}
fn onNullMessage(
userdata: ?*void,
connection: *nats.Connection,
subscription: *nats.Subscription,
message: *nats.Message,
) void {
_ = subscription;
_ = userdata;
if (message.getReply()) |reply| {
connection.publish(reply, "greetings") catch @panic("OH NO");
} else @panic("HOW");
}
fn onNullClose(userdata: ?*void) void {
_ = userdata;
}
test "nats.Subscription (async, null)" {
var server = try util.TestServer.launch(.{});
defer server.stop();
try nats.init(nats.default_spin_count);
defer nats.deinit();
const connection = try nats.Connection.connectTo(server.url);
defer connection.destroy();
const message_subject: [:0]const u8 = "hello";
const message_reply: [:0]const u8 = "reply";
const message_data: [:0]const u8 = "world";
const message = try nats.Message.create(message_subject, message_reply, message_data);
defer message.destroy();
{
{
const subscription = try connection.subscribe(?*void, message_subject, onNullMessage, null);
defer subscription.destroy();
try subscription.setCompletionCallback(?*void, onNullClose, null);
const response = try connection.requestMessage(message, 1000);
try std.testing.expectEqualStrings(
"greetings",
response.getData() orelse return error.TestUnexpectedResult,
);
}
// we have to sleep to allow the close callback to run.
nats.sleep(1);
}
}