From b3a6b73306c73b575bbbd38ba834e80e24fa9a65 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Sat, 8 Jul 2023 03:33:57 +0800 Subject: [PATCH] eventbus: add generic support for event name (#18805) --- .../modules/some_module/some_module.v | 4 +- vlib/eventbus/README.md | 21 +++-- vlib/eventbus/eventbus.v | 92 +++++++++++-------- vlib/eventbus/eventbus_test.v | 57 ++++++++++-- ...r_param_call_with_nonpointer_rvalue_test.v | 4 +- 5 files changed, 119 insertions(+), 59 deletions(-) diff --git a/examples/eventbus/modules/some_module/some_module.v b/examples/eventbus/modules/some_module/some_module.v index 58e3dede827e3d..5fc4af58185cf3 100644 --- a/examples/eventbus/modules/some_module/some_module.v +++ b/examples/eventbus/modules/some_module/some_module.v @@ -3,7 +3,7 @@ module some_module import eventbus const ( - eb = eventbus.new() + eb = eventbus.new[string]() ) pub struct Duration { @@ -29,6 +29,6 @@ pub fn do_work() { some_module.eb.publish('event_baz', &Duration{42}, &EventMetadata{'Additional data at the end.'}) } -pub fn get_subscriber() eventbus.Subscriber { +pub fn get_subscriber() eventbus.Subscriber[string] { return *some_module.eb.subscriber } diff --git a/vlib/eventbus/README.md b/vlib/eventbus/README.md index 3e49f3791081ad..6f39ecc3b84ef2 100644 --- a/vlib/eventbus/README.md +++ b/vlib/eventbus/README.md @@ -4,26 +4,27 @@ A module to provide eventing capabilities using pub/sub. ## API -1. `new()` - create a new `EventBus` +1. `new[T]()` - create a new `EventBus` +2. `EventBus.new[T]()` - create a new `EventBus` ### Structs: **EventBus:** -1. `publish(name string, sender voidptr, args voidptr)` - publish an event with provided +1. `publish(name T, sender voidptr, args voidptr)` - publish an event with provided Params & name 2. `clear_all()` - clear all subscribers -3. `has_subscriber(name string)` - check if a subscriber to an event exists +3. `has_subscriber(name T)` - check if a subscriber to an event exists **Subscriber:** -1. `subscribe(name string, handler EventHandlerFn)` - subscribe to an event -2. `subscribe_once(name string, handler EventHandlerFn)` - subscribe only once to an event -3. `subscribe_method(name string, handler EventHandlerFn, receiver voidptr)` - subscribe to +1. `subscribe(name T, handler EventHandlerFn)` - subscribe to an event +2. `subscribe_once(name T, handler EventHandlerFn)` - subscribe only once to an event +3. `subscribe_method(name T, handler EventHandlerFn, receiver voidptr)` - subscribe to an event and also set the `receiver` as a parameter. Since it's not yet possible to send methods as parameters, this is a workaround. -4. `is_subscribed(name string)` - check if we are subscribed to an event -5. `unsubscribe(name string)` - unsubscribe from an event +4. `is_subscribed(name T)` - check if we are subscribed to an event +5. `unsubscribe(name T)` - unsubscribe from an event **Event Handler Signature:** @@ -63,7 +64,7 @@ import eventbus // initialize it globally const ( - eb = eventbus.new() + eb = eventbus.new[string]() ) fn main() { @@ -88,7 +89,7 @@ module main import eventbus -const eb = eventbus.new() +const eb = eventbus.new[string]() struct Work { hours int diff --git a/vlib/eventbus/eventbus.v b/vlib/eventbus/eventbus.v index f85cf792d514b0..1b5baf951a4052 100644 --- a/vlib/eventbus/eventbus.v +++ b/vlib/eventbus/eventbus.v @@ -2,59 +2,70 @@ module eventbus pub type EventHandlerFn = fn (receiver voidptr, args voidptr, sender voidptr) -pub struct Publisher { +pub struct Publisher[T] { mut: - registry &Registry = unsafe { nil } + registry &Registry[T] = unsafe { nil } } -pub struct Subscriber { +pub struct Subscriber[T] { mut: - registry &Registry = unsafe { nil } + registry &Registry[T] = unsafe { nil } } -struct Registry { +struct Registry[T] { mut: - events []EventHandler + events []EventHandler[T] } -struct EventHandler { - name string +struct EventHandler[T] { + name T handler EventHandlerFn receiver voidptr = unsafe { nil } once bool } -pub struct EventBus { +pub struct EventBus[T] { pub mut: - registry &Registry = unsafe { nil } - publisher &Publisher = unsafe { nil } - subscriber &Subscriber = unsafe { nil } + registry &Registry[T] = unsafe { nil } + publisher &Publisher[T] = unsafe { nil } + subscriber &Subscriber[T] = unsafe { nil } } -pub fn new() &EventBus { - registry := &Registry{ +// EventBus.new[T] create a new eventbus with event type T. +pub fn EventBus.new[T]() &EventBus[T] { + registry := &Registry[T]{ events: [] } - return &EventBus{registry, &Publisher{registry}, &Subscriber{registry}} + return &EventBus[T]{registry, &Publisher[T]{registry}, &Subscriber[T]{registry}} } -// EventBus Methods -pub fn (eb &EventBus) publish(name string, sender voidptr, args voidptr) { +// new[T] create a new eventbus with event type T. +pub fn new[T]() &EventBus[T] { + registry := &Registry[T]{ + events: [] + } + return &EventBus[T]{registry, &Publisher[T]{registry}, &Subscriber[T]{registry}} +} + +// publish publish an event with provided Params & name. +pub fn (eb &EventBus[T]) publish(name T, sender voidptr, args voidptr) { mut publisher := eb.publisher publisher.publish(name, sender, args) } -pub fn (eb &EventBus) clear_all() { +// clear_all clear all subscribers. +pub fn (eb &EventBus[T]) clear_all() { mut publisher := eb.publisher publisher.clear_all() } -pub fn (eb &EventBus) has_subscriber(name string) bool { +// has_subscriber check if a subscriber to an event exists. +pub fn (eb &EventBus[T]) has_subscriber(name T) bool { return eb.registry.check_subscriber(name) } -// Publisher Methods -fn (mut pb Publisher) publish(name string, sender voidptr, args voidptr) { +// publish publish an event with provided Params & name. +fn (mut pb Publisher[T]) publish(name T, sender voidptr, args voidptr) { for event in pb.registry.events { if event.name == name { event.handler(event.receiver, args, sender) @@ -63,59 +74,64 @@ fn (mut pb Publisher) publish(name string, sender voidptr, args voidptr) { pb.registry.events = pb.registry.events.filter(!(it.name == name && it.once)) } -fn (mut p Publisher) clear_all() { +// clear_all clear all subscribers. +fn (mut p Publisher[T]) clear_all() { p.registry.events.clear() } -// Subscriber Methods -pub fn (mut s Subscriber) subscribe(name string, handler EventHandlerFn) { - s.registry.events << EventHandler{ +// subscribe subscribe to an event `name`. +pub fn (mut s Subscriber[T]) subscribe(name T, handler EventHandlerFn) { + s.registry.events << EventHandler[T]{ name: name handler: handler } } -pub fn (mut s Subscriber) subscribe_method(name string, handler EventHandlerFn, receiver voidptr) { - s.registry.events << EventHandler{ +// subscribe_method subscribe to an event `name` and also set the `receiver` as a parameter. +pub fn (mut s Subscriber[T]) subscribe_method(name T, handler EventHandlerFn, receiver voidptr) { + s.registry.events << EventHandler[T]{ name: name handler: handler receiver: receiver } } -// unsubscribe_method unsubscribe a receiver for only one method -pub fn (mut s Subscriber) unsubscribe_method(name string, receiver voidptr) { +// unsubscribe_method unsubscribe a receiver for only one method. +pub fn (mut s Subscriber[T]) unsubscribe_method(name T, receiver voidptr) { s.registry.events = s.registry.events.filter(!(it.name == name && it.receiver == receiver)) } -// unsubscribe_receiver unsubscribes a receiver from all events -pub fn (mut s Subscriber) unsubscribe_receiver(receiver voidptr) { +// unsubscribe_receiver unsubscribes a receiver from all events. +pub fn (mut s Subscriber[T]) unsubscribe_receiver(receiver voidptr) { s.registry.events = s.registry.events.filter(it.receiver != receiver) } -pub fn (mut s Subscriber) subscribe_once(name string, handler EventHandlerFn) { - s.registry.events << EventHandler{ +// subscribe_once subscribe only once to an event `name`. +pub fn (mut s Subscriber[T]) subscribe_once(name T, handler EventHandlerFn) { + s.registry.events << EventHandler[T]{ name: name handler: handler once: true } } -pub fn (s &Subscriber) is_subscribed(name string) bool { +// is_subscribed check if we are subscribed to an event `name`. +pub fn (s &Subscriber[T]) is_subscribed(name T) bool { return s.registry.check_subscriber(name) } -// is_subscribed_method checks whether a receiver was already subscribed for any events -pub fn (s &Subscriber) is_subscribed_method(name string, receiver voidptr) bool { +// is_subscribed_method checks whether a receiver was already subscribed for any events. +pub fn (s &Subscriber[T]) is_subscribed_method(name T, receiver voidptr) bool { return s.registry.events.any(it.name == name && it.receiver == receiver) } -pub fn (mut s Subscriber) unsubscribe(name string, handler EventHandlerFn) { +// unsubscribe unsubscribe from an event `name`. +pub fn (mut s Subscriber[T]) unsubscribe(name T, handler EventHandlerFn) { // v := voidptr(handler) s.registry.events = s.registry.events.filter(!(it.name == name && it.handler == handler)) } // Registry Methods -fn (r &Registry) check_subscriber(name string) bool { +fn (r &Registry[T]) check_subscriber(name T) bool { return r.events.any(it.name == name) } diff --git a/vlib/eventbus/eventbus_test.v b/vlib/eventbus/eventbus_test.v index e27af886fe86c1..56fca7ad97d6ec 100644 --- a/vlib/eventbus/eventbus_test.v +++ b/vlib/eventbus/eventbus_test.v @@ -8,11 +8,12 @@ struct FakeReceiver { ok bool } -fn test_eventbus() { +fn test_eventbus_string() { ev_data := &EventData{'hello'} - mut eb := eventbus.new() + mut eb := eventbus.new[string]() eb.subscriber.subscribe_once('on_test', on_test) assert eb.has_subscriber('on_test') + assert !eb.has_subscriber('not_exist') assert eb.subscriber.is_subscribed('on_test') eb.publish('on_test', eb, ev_data) assert !eb.has_subscriber('on_test') @@ -25,10 +26,52 @@ fn test_eventbus() { assert !eb.subscriber.is_subscribed('on_test') } +enum Events { + event_1 + event_2 + event_3 +} + +fn test_eventbus_enum() { + ev_data := &EventData{'hello'} + mut eb := eventbus.EventBus.new[Events]() + eb.subscriber.subscribe_once(Events.event_1, on_test) + assert eb.has_subscriber(Events.event_1) + assert !eb.has_subscriber(Events.event_2) + assert eb.subscriber.is_subscribed(Events.event_1) + eb.publish(Events.event_1, eb, ev_data) + assert !eb.has_subscriber(Events.event_1) + assert !eb.subscriber.is_subscribed(Events.event_1) + eb.subscriber.subscribe(Events.event_1, on_test) + assert eb.has_subscriber(Events.event_1) + assert eb.subscriber.is_subscribed(Events.event_1) + eb.clear_all() + assert !eb.has_subscriber(Events.event_1) + assert !eb.subscriber.is_subscribed(Events.event_1) +} + +fn test_eventbus_int() { + ev_data := &EventData{'hello'} + mut eb := eventbus.EventBus.new[int]() + eb.subscriber.subscribe_once(9999, on_test) + assert eb.has_subscriber(9999) + assert !eb.has_subscriber(1111) + assert eb.subscriber.is_subscribed(9999) + eb.publish(9999, eb, ev_data) + assert !eb.has_subscriber(9999) + assert !eb.subscriber.is_subscribed(9999) + eb.subscriber.subscribe(9999, on_test) + assert eb.has_subscriber(9999) + assert eb.subscriber.is_subscribed(9999) + eb.clear_all() + assert !eb.has_subscriber(9999) + assert !eb.subscriber.is_subscribed(9999) +} + fn test_subscribe_method() { // Does not really test subscribe_method idinvidually though // given - mut eb := eventbus.new() + mut eb := eventbus.new[string]() r := FakeReceiver{} assert !eb.subscriber.is_subscribed_method('on_test_with_receiver', r) @@ -41,7 +84,7 @@ fn test_subscribe_method() { fn test_unsubscribe_method() { // given - mut eb := eventbus.new() + mut eb := eventbus.new[string]() r := FakeReceiver{} r2 := FakeReceiver{} @@ -58,7 +101,7 @@ fn test_unsubscribe_method() { fn test_publish() { // given ev_data := &EventData{'hello'} - mut eb := eventbus.new() + mut eb := eventbus.new[string]() // when eb.subscriber.subscribe_once('on_test', on_test) @@ -71,7 +114,7 @@ fn test_publish() { fn test_publish_with_receiver() { // given - mut eb := eventbus.new() + mut eb := eventbus.new[string]() ev_data := &EventData{'hello'} r := FakeReceiver{} @@ -85,7 +128,7 @@ fn test_publish_with_receiver() { fn test_unsubscribe_reveiver() { // given - mut eb := eventbus.new() + mut eb := eventbus.new[string]() r := &FakeReceiver{} // when diff --git a/vlib/v/tests/fn_voidptr_param_call_with_nonpointer_rvalue_test.v b/vlib/v/tests/fn_voidptr_param_call_with_nonpointer_rvalue_test.v index 27a8192f4d311a..ad7e28be8e7fb2 100644 --- a/vlib/v/tests/fn_voidptr_param_call_with_nonpointer_rvalue_test.v +++ b/vlib/v/tests/fn_voidptr_param_call_with_nonpointer_rvalue_test.v @@ -5,7 +5,7 @@ struct MyMessage { } fn test_fn_call_with_nonpointer_rvalue() { - eb := eventbus.new() + eb := eventbus.new[string]() mut subscriber := eb.subscriber subscriber.subscribe('my_publish', subscriber_method) @@ -18,6 +18,6 @@ fn subscriber_method(receiver voidptr, ev &MyMessage, sender voidptr) { println(ev) } -fn do_something(eb &eventbus.EventBus) { +fn do_something[T](eb &eventbus.EventBus[T]) { eb.publish('my_publish', eb, MyMessage{ msg: 'this is my message' }) }