Application Context Implementation #4

Closed
kayomn wants to merge 93 commits from event-loop-dev into main
3 changed files with 36 additions and 17 deletions
Showing only changes of commit 2e544393a5 - Show all commits

View File

@ -377,23 +377,19 @@ test "Data swapping" {
} }
/// ///
/// Returns a [Writer] that silently consumes all given data without failure and throws it away. /// [Writer] that silently consumes all given data without failure and throws it away.
/// ///
/// This is commonly used for testing or redirected otherwise unwanted output data that has to be /// This is commonly used for testing or redirected otherwise unwanted output data that has to be
/// sent somewhere for whatever reason. /// sent somewhere for whatever reason.
/// ///
pub fn nullWriter() Writer { pub const null_writer = Writer.from(struct {
var dummy: usize = 0; fn write(buffer: []const u8) usize {
return Writer.capture(&dummy, struct {
fn write(_: *usize, buffer: []const u8) usize {
return buffer.len; return buffer.len;
} }
}.write); }.write);
}
test "Null writing" { test "Null writing" {
const sequence = "foo"; const sequence = "foo";
try testing.expect(nullWriter().call(sequence) == sequence.len); try testing.expect(null_writer.call(sequence) == sequence.len);
} }

View File

@ -18,6 +18,10 @@ pub fn Function(comptime In: type, comptime Out: type) type {
callErased: fn (*anyopaque, In) Out, callErased: fn (*anyopaque, In) Out,
context: *anyopaque, context: *anyopaque,
fn Invoker(comptime Context: type) type {
return if (Context == void) fn (In) Out else fn (Context, In) Out;
}
/// ///
/// Function type. /// Function type.
/// ///
@ -26,23 +30,42 @@ pub fn Function(comptime In: type, comptime Out: type) type {
/// ///
/// Invokes `self` with `input`, producing a result according to the current context data. /// Invokes `self` with `input`, producing a result according to the current context data.
/// ///
pub fn call(self: *Self, input: In) Out { pub fn call(self: Self, input: In) Out {
return self.callErased(self.context, input); return self.callErased(self.context, input);
} }
/// ///
/// Creates a new [Self] by capturing the `context` value as the capture context and /// Creates and returns a [Self] using the `invoke` as the behavior executed when [call] or
/// [callErased] is called.
///
/// For creating a closure-style function, see [fromClosure].
///
pub fn from(comptime invoke: fn (In) Out) Self {
return .{
.context = undefined,
.callErased = struct {
fn callErased(_: *anyopaque, input: In) Out {
return invoke(input);
}
}.callErased,
};
}
///
/// Creates and returns a [Self] by capturing the `context` value as the capture context and
/// `invoke` as the behavior executed when [call] or [callErased] is called. /// `invoke` as the behavior executed when [call] or [callErased] is called.
/// ///
/// The newly created [Self] is returned. /// The newly created [Self] is returned.
/// ///
pub fn capture(context: anytype, comptime invoke: fn (@TypeOf(context), In) Out) Self { pub fn fromClosure(context: anytype, comptime invoke: fn (@TypeOf(context), In) Out) Self {
const Context = @TypeOf(context); const Context = @TypeOf(context);
switch (@typeInfo(Context)) { switch (@typeInfo(Context)) {
.Pointer => |info| if (info.size == .Slice) .Pointer => |info| if (info.size == .Slice)
@compileError("`context` cannot be a slice"), @compileError("`context` cannot be a slice"),
.Void => {},
else => @compileError("`context` must be a pointer"), else => @compileError("`context` must be a pointer"),
} }
@ -51,8 +74,8 @@ pub fn Function(comptime In: type, comptime Out: type) type {
.callErased = struct { .callErased = struct {
fn callErased(erased: *anyopaque, input: In) Out { fn callErased(erased: *anyopaque, input: In) Out {
return invoke(@ptrCast(*Context, @alignCast( return if (Context == void) invoke(input) else invoke(@ptrCast(
@alignOf(Context), erased)).*, input); *Context, @alignCast(@alignOf(Context), erased)).*, input);
} }
}.callErased, }.callErased,
}; };

View File

@ -122,7 +122,7 @@ pub const PushError = io.Allocator.MakeError;
/// referenced by `fixed_stack` until it is full. /// referenced by `fixed_stack` until it is full.
/// ///
pub fn fixedWriter(fixed_stack: *Fixed(u8)) io.Writer { pub fn fixedWriter(fixed_stack: *Fixed(u8)) io.Writer {
return io.Writer.capture(fixed_stack, struct { return io.Writer.fromClosure(fixed_stack, struct {
fn write(stack: *Fixed(u8), buffer: []const u8) usize { fn write(stack: *Fixed(u8), buffer: []const u8) usize {
stack.pushAll(buffer) catch |err| switch (err) { stack.pushAll(buffer) catch |err| switch (err) {
error.OutOfMemory => return 0, error.OutOfMemory => return 0,