From b05004291e418dd206e255a0b0eb8b6d4c9e329c Mon Sep 17 00:00:00 2001 From: renerocksai Date: Sun, 30 Mar 2025 12:20:43 +0200 Subject: [PATCH] refactored endpoint Binder/Bind/Bound/Interface ... namings --- src/App.zig | 63 +++++++++++++++++++------------------- src/endpoint.zig | 78 +++++++++++++++++++++++++----------------------- 2 files changed, 73 insertions(+), 68 deletions(-) diff --git a/src/App.zig b/src/App.zig index 9314ae2..ad0740f 100644 --- a/src/App.zig +++ b/src/App.zig @@ -32,7 +32,6 @@ pub fn Create(comptime Context: type) type { context: *Context = undefined, gpa: Allocator = undefined, opts: AppOpts = undefined, - // endpoints: std.StringArrayHashMapUnmanaged(*Endpoint.Interface) = .empty, endpoints: std.ArrayListUnmanaged(*Endpoint.Interface) = .empty, there_can_be_only_one: bool = false, @@ -61,64 +60,68 @@ pub fn Create(comptime Context: type) type { path: []const u8, destroy: *const fn (*Interface, Allocator) void = undefined, }; - pub fn Wrap(T: type) type { + pub fn Bind(ArbitraryEndpoint: type) type { return struct { - wrapped: *T, + endpoint: *ArbitraryEndpoint, interface: Interface, // tbh: unnecessary, since we have it in _static app_context: *Context, - const Wrapped = @This(); + const Bound = @This(); - pub fn unwrap(interface: *Interface) *Wrapped { - const self: *Wrapped = @alignCast(@fieldParentPtr("interface", interface)); + pub fn unwrap(interface: *Interface) *Bound { + const self: *Bound = @alignCast(@fieldParentPtr("interface", interface)); return self; } pub fn destroy(interface: *Interface, allocator: Allocator) void { - const self: *Wrapped = @alignCast(@fieldParentPtr("interface", interface)); + const self: *Bound = @alignCast(@fieldParentPtr("interface", interface)); allocator.destroy(self); } - pub fn onRequestWrapped(interface: *Interface, r: Request) !void { - var self: *Wrapped = Wrapped.unwrap(interface); + pub fn onRequestInterface(interface: *Interface, r: Request) !void { + var self: *Bound = Bound.unwrap(interface); var arena = try get_arena(); try self.onRequest(arena.allocator(), self.app_context, r); _ = arena.reset(.{ .retain_with_limit = _static.opts.arena_retain_capacity }); } - pub fn onRequest(self: *Wrapped, arena: Allocator, app_context: *Context, r: Request) !void { + pub fn onRequest(self: *Bound, arena: Allocator, app_context: *Context, r: Request) !void { const ret = switch (r.methodAsEnum()) { - .GET => self.wrapped.*.get(arena, app_context, r), - .POST => self.wrapped.*.post(arena, app_context, r), - .PUT => self.wrapped.*.put(arena, app_context, r), - .DELETE => self.wrapped.*.delete(arena, app_context, r), - .PATCH => self.wrapped.*.patch(arena, app_context, r), - .OPTIONS => self.wrapped.*.options(arena, app_context, r), + .GET => self.endpoint.*.get(arena, app_context, r), + .POST => self.endpoint.*.post(arena, app_context, r), + .PUT => self.endpoint.*.put(arena, app_context, r), + .DELETE => self.endpoint.*.delete(arena, app_context, r), + .PATCH => self.endpoint.*.patch(arena, app_context, r), + .OPTIONS => self.endpoint.*.options(arena, app_context, r), else => error.UnsupportedHtmlRequestMethod, }; if (ret) { // handled without error } else |err| { - switch (self.wrapped.*.error_strategy) { + switch (self.endpoint.*.error_strategy) { .raise => return err, .log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505), - .log_to_console => zap.debug("Error in {} {s} : {}", .{ Wrapped, r.method orelse "(no method)", err }), + .log_to_console => zap.debug( + "Error in {} {s} : {}", + .{ Bound, r.method orelse "(no method)", err }, + ), } } } }; } - pub fn init(T: type, value: *T) Endpoint.Wrap(T) { - checkEndpointType(T); + pub fn init(ArbitraryEndpoint: type, endpoint: *ArbitraryEndpoint) Endpoint.Bind(ArbitraryEndpoint) { + checkEndpointType(ArbitraryEndpoint); + const BoundEp = Endpoint.Bind(ArbitraryEndpoint); return .{ - .wrapped = value, + .endpoint = endpoint, .interface = .{ - .path = value.path, - .call = Endpoint.Wrap(T).onRequestWrapped, - .destroy = Endpoint.Wrap(T).destroy, + .path = endpoint.path, + .call = BoundEp.onRequestInterface, + .destroy = BoundEp.destroy, }, .app_context = _static.context, }; @@ -262,9 +265,9 @@ pub fn Create(comptime Context: type) type { } const EndpointType = @typeInfo(@TypeOf(endpoint)).pointer.child; Endpoint.checkEndpointType(EndpointType); - const wrapper = try _static.gpa.create(Endpoint.Wrap(EndpointType)); - wrapper.* = Endpoint.init(EndpointType, endpoint); - try _static.endpoints.append(_static.gpa, &wrapper.interface); + const bound = try _static.gpa.create(Endpoint.Bind(EndpointType)); + bound.* = Endpoint.init(EndpointType, endpoint); + try _static.endpoints.append(_static.gpa, &bound.interface); } pub fn listen(_: *App, l: ListenerSettings) !void { @@ -284,9 +287,9 @@ pub fn Create(comptime Context: type) type { fn onRequest(r: Request) !void { if (r.path) |p| { - for (_static.endpoints.items) |wrapper| { - if (std.mem.startsWith(u8, p, wrapper.path)) { - return try wrapper.call(wrapper, r); + for (_static.endpoints.items) |interface| { + if (std.mem.startsWith(u8, p, interface.path)) { + return try interface.call(interface, r); } } } diff --git a/src/endpoint.zig b/src/endpoint.zig index 6ca970b..c32a489 100644 --- a/src/endpoint.zig +++ b/src/endpoint.zig @@ -106,66 +106,68 @@ pub fn checkEndpointType(T: type) void { } } -pub const Wrapper = struct { +pub const Binder = struct { pub const Interface = struct { call: *const fn (*Interface, zap.Request) anyerror!void = undefined, path: []const u8, - destroy: *const fn (allocator: std.mem.Allocator, *Interface) void = undefined, + destroy: *const fn (*Interface, std.mem.Allocator) void = undefined, }; - pub fn Wrap(T: type) type { + pub fn Bind(ArbitraryEndpoint: type) type { return struct { - wrapped: *T, - wrapper: Interface, + endpoint: *ArbitraryEndpoint, + interface: Interface, - const Wrapped = @This(); + const Bound = @This(); - pub fn unwrap(wrapper: *Interface) *Wrapped { - const self: *Wrapped = @alignCast(@fieldParentPtr("wrapper", wrapper)); + pub fn unwrap(interface: *Interface) *Bound { + const self: *Bound = @alignCast(@fieldParentPtr("interface", interface)); return self; } - pub fn destroy(allocator: std.mem.Allocator, wrapper: *Interface) void { - const self: *Wrapped = @alignCast(@fieldParentPtr("wrapper", wrapper)); + pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { + const self: *Bound = @alignCast(@fieldParentPtr("interface", interface)); allocator.destroy(self); } - pub fn onRequestWrapped(wrapper: *Interface, r: zap.Request) !void { - var self: *Wrapped = Wrapped.unwrap(wrapper); + pub fn onRequestInterface(interface: *Interface, r: zap.Request) !void { + var self: *Bound = Bound.unwrap(interface); try self.onRequest(r); } - pub fn onRequest(self: *Wrapped, r: zap.Request) !void { + pub fn onRequest(self: *Bound, r: zap.Request) !void { const ret = switch (r.methodAsEnum()) { - .GET => self.wrapped.*.get(r), - .POST => self.wrapped.*.post(r), - .PUT => self.wrapped.*.put(r), - .DELETE => self.wrapped.*.delete(r), - .PATCH => self.wrapped.*.patch(r), - .OPTIONS => self.wrapped.*.options(r), + .GET => self.endpoint.*.get(r), + .POST => self.endpoint.*.post(r), + .PUT => self.endpoint.*.put(r), + .DELETE => self.endpoint.*.delete(r), + .PATCH => self.endpoint.*.patch(r), + .OPTIONS => self.endpoint.*.options(r), else => error.UnsupportedHtmlRequestMethod, }; if (ret) { // handled without error } else |err| { - switch (self.wrapped.*.error_strategy) { + switch (self.endpoint.*.error_strategy) { .raise => return err, .log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505), - .log_to_console => zap.debug("Error in {} {s} : {}", .{ Wrapped, r.method orelse "(no method)", err }), + .log_to_console => zap.debug("Error in {} {s} : {}", .{ Bound, r.method orelse "(no method)", err }), } } } }; } - pub fn init(T: type, value: *T) Wrapper.Wrap(T) { - checkEndpointType(T); - var ret: Wrapper.Wrap(T) = .{ - .wrapped = value, - .wrapper = .{ .path = value.path }, + pub fn init(ArbitraryEndpoint: type, value: *ArbitraryEndpoint) Binder.Bind(ArbitraryEndpoint) { + checkEndpointType(ArbitraryEndpoint); + const BoundEp = Binder.Bind(ArbitraryEndpoint); + return .{ + .endpoint = value, + .interface = .{ + .path = value.path, + .call = BoundEp.onRequestInterface, + .destroy = BoundEp.destroy, + }, }; - ret.wrapper.call = Wrapper.Wrap(T).onRequestWrapped; - ret.wrapper.destroy = Wrapper.Wrap(T).destroy; - return ret; } }; @@ -262,7 +264,7 @@ pub const Listener = struct { allocator: std.mem.Allocator, /// Internal static interface struct of member endpoints - var endpoints: std.ArrayListUnmanaged(*Wrapper.Interface) = .empty; + var endpoints: std.ArrayListUnmanaged(*Binder.Interface) = .empty; /// Internal, static request handler callback. Will be set to the optional, /// user-defined request callback that only gets called if no endpoints match @@ -296,8 +298,8 @@ pub const Listener = struct { /// Registered endpoints will not be de-initialized automatically; just removed /// from the internal map. pub fn deinit(self: *Listener) void { - for (endpoints.items) |endpoint_wrapper| { - endpoint_wrapper.destroy(self.allocator, endpoint_wrapper); + for (endpoints.items) |interface| { + interface.destroy(interface, self.allocator); } endpoints.deinit(self.allocator); } @@ -328,16 +330,16 @@ pub const Listener = struct { } const EndpointType = @typeInfo(@TypeOf(e)).pointer.child; checkEndpointType(EndpointType); - const wrapper = try self.allocator.create(Wrapper.Wrap(EndpointType)); - wrapper.* = Wrapper.init(EndpointType, e); - try endpoints.append(self.allocator, &wrapper.wrapper); + const bound = try self.allocator.create(Binder.Bind(EndpointType)); + bound.* = Binder.init(EndpointType, e); + try endpoints.append(self.allocator, &bound.interface); } fn onRequest(r: Request) !void { if (r.path) |p| { - for (endpoints.items) |wrapper| { - if (std.mem.startsWith(u8, p, wrapper.path)) { - return try wrapper.call(wrapper, r); + for (endpoints.items) |interface| { + if (std.mem.startsWith(u8, p, interface.path)) { + return try interface.call(interface, r); } } }