You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
3.0 KiB

const std = @import("std");
const Sink = @import("sink.zig").Sink;
const Delegate = @import("delegate.zig").Delegate;
pub fn Signal(comptime Event: type) type {
return struct {
const Self = @This();
calls: std.ArrayList(Delegate(Event)),
allocator: ?*std.mem.Allocator = null,
pub fn init(allocator: *std.mem.Allocator) Self {
// we purposely do not store the allocator locally in this case so we know not to destroy ourself in deint!
return Self{
.calls = std.ArrayList(Delegate(Event)).init(allocator),
};
}
/// heap allocates a Signal
pub fn create(allocator: *std.mem.Allocator) *Self {
var signal = allocator.create(Self) catch unreachable;
signal.calls = std.ArrayList(Delegate(Event)).init(allocator);
signal.allocator = allocator;
return signal;
}
pub fn deinit(self: *Self) void {
self.calls.deinit();
// optionally destroy ourself as well if we came from an allocator
if (self.allocator) |allocator| allocator.destroy(self);
}
pub fn size(self: Self) usize {
return self.calls.items.len;
}
pub fn empty(self: Self) bool {
return self.size == 0;
}
/// Disconnects all the listeners from a signal
pub fn clear(self: *Self) void {
self.calls.items.len = 0;
}
pub fn publish(self: Self, arg: Event) void {
for (self.calls.items) |call| {
call.trigger(arg);
}
}
/// Constructs a sink that is allowed to modify a given signal
pub fn sink(self: *Self) Sink(Event) {
return Sink(Event).init(self);
}
};
}
fn tester(param: u32) void {
std.testing.expectEqual(@as(u32, 666), param) catch unreachable;
}
const Thing = struct {
field: f32 = 0,
pub fn tester(_: *Thing, param: u32) void {
std.testing.expectEqual(@as(u32, 666), param) catch unreachable;
}
};
test "Signal/Sink" {
var signal = Signal(u32).init(std.testing.allocator);
defer signal.deinit();
var sink = signal.sink();
sink.connect(tester);
try std.testing.expectEqual(@as(usize, 1), signal.size());
// bound listener
var thing = Thing{};
sink.connectBound(&thing, "tester");
signal.publish(666);
sink.disconnect(tester);
signal.publish(666);
try std.testing.expectEqual(@as(usize, 1), signal.size());
sink.disconnectBound(&thing);
try std.testing.expectEqual(@as(usize, 0), signal.size());
}
test "Sink Before null" {
var signal = Signal(u32).init(std.testing.allocator);
defer signal.deinit();
var sink = signal.sink();
sink.connect(tester);
try std.testing.expectEqual(@as(usize, 1), signal.size());
var thing = Thing{};
sink.before(null).connectBound(&thing, "tester");
try std.testing.expectEqual(@as(usize, 2), signal.size());
}