116 lines
3.7 KiB
Zig
Raw Normal View History

2020-05-31 21:28:29 -07:00
const std = @import("std");
const Signal = @import("signal.zig").Signal;
const Delegate = @import("delegate.zig").Delegate;
/// helper used to connect and disconnect listeners on the fly from a Signal. Listeners are wrapped in Delegates
/// and can be either free functions or functions bound to a struct.
pub fn Sink(comptime Event: type) type {
return struct {
const Self = @This();
2020-06-07 17:28:49 -07:00
insert_index: usize,
2020-05-31 21:28:29 -07:00
/// the Signal this Sink is temporarily wrapping
var owning_signal: *Signal(Event) = undefined;
pub fn init(signal: *Signal(Event)) Self {
owning_signal = signal;
2020-06-07 17:28:49 -07:00
return Self{ .insert_index = owning_signal.calls.items.len };
}
pub fn before(self: Self, callback: ?fn (Event) void) Self {
if (callback) |cb| {
if (self.indexOf(cb)) |index| {
return Self{ .insert_index = index };
}
}
return self;
}
2020-07-20 09:53:15 -07:00
pub fn beforeBound(self: Self, ctx: anytype) Self {
2020-06-07 17:28:49 -07:00
if (@typeInfo(@TypeOf(ctx)) == .Pointer) {
if (self.indexOfBound(ctx)) |index| {
return Self{ .insert_index = index };
}
}
return self;
2020-05-31 21:28:29 -07:00
}
pub fn connect(self: Self, callback: fn (Event) void) void {
2020-06-07 17:28:49 -07:00
std.debug.assert(self.indexOf(callback) == null);
_ = owning_signal.calls.insert(self.insert_index, Delegate(Event).initFree(callback)) catch unreachable;
2020-05-31 21:28:29 -07:00
}
2020-07-20 09:53:15 -07:00
pub fn connectBound(self: Self, ctx: anytype, comptime fn_name: []const u8) void {
2020-06-07 17:28:49 -07:00
std.debug.assert(self.indexOfBound(ctx) == null);
_ = owning_signal.calls.insert(self.insert_index, Delegate(Event).initBound(ctx, fn_name)) catch unreachable;
2020-05-31 21:28:29 -07:00
}
pub fn disconnect(self: Self, callback: fn (Event) void) void {
2020-06-07 17:28:49 -07:00
if (self.indexOf(callback)) |index| {
_ = owning_signal.calls.swapRemove(index);
}
}
2020-07-20 09:53:15 -07:00
pub fn disconnectBound(self: Self, ctx: anytype) void {
2020-06-07 17:28:49 -07:00
if (self.indexOfBound(ctx)) |index| {
_ = owning_signal.calls.swapRemove(index);
}
}
fn indexOf(self: Self, callback: fn (Event) void) ?usize {
2020-05-31 21:28:29 -07:00
for (owning_signal.calls.items) |call, i| {
if (call.containsFree(callback)) {
2020-06-07 17:28:49 -07:00
return i;
2020-05-31 21:28:29 -07:00
}
}
2020-06-07 17:28:49 -07:00
return null;
2020-05-31 21:28:29 -07:00
}
2020-07-20 09:53:15 -07:00
fn indexOfBound(self: Self, ctx: anytype) ?usize {
2020-05-31 21:28:29 -07:00
for (owning_signal.calls.items) |call, i| {
if (call.containsBound(ctx)) {
2020-06-07 17:28:49 -07:00
return i;
2020-05-31 21:28:29 -07:00
}
}
2020-06-07 17:28:49 -07:00
return null;
2020-05-31 21:28:29 -07:00
}
};
}
2020-06-07 17:28:49 -07:00
fn tester(param: u32) void {
std.testing.expectEqual(@as(u32, 666), param);
}
const Thing = struct {
field: f32 = 0,
pub fn tester(self: *Thing, param: u32) void {
std.testing.expectEqual(@as(u32, 666), param);
}
};
test "Sink Before free" {
var signal = Signal(u32).init(std.testing.allocator);
defer signal.deinit();
signal.sink().connect(tester);
std.testing.expectEqual(signal.sink().indexOf(tester).?, 0);
var thing = Thing{};
signal.sink().before(tester).connectBound(&thing, "tester");
std.testing.expectEqual(signal.sink().indexOfBound(&thing).?, 0);
}
test "Sink Before bound" {
var signal = Signal(u32).init(std.testing.allocator);
defer signal.deinit();
var thing = Thing{};
signal.sink().connectBound(&thing, "tester");
std.testing.expectEqual(signal.sink().indexOfBound(&thing).?, 0);
signal.sink().beforeBound(&thing).connect(tester);
std.testing.expectEqual(signal.sink().indexOf(tester).?, 0);
2020-07-20 09:53:15 -07:00
}