From 0382331499f446184799ec09e8386c328149e902 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Sat, 23 Nov 2019 16:24:25 +0500 Subject: [PATCH] vlib: add eventbus module --- vlib/eventbus/eventbus.v | 116 +++++++++++++++++++++++++++++++ vlib/eventbus/eventbus_test.v | 42 +++++++++++ vlib/eventbus/params.v | 127 ++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 vlib/eventbus/eventbus.v create mode 100644 vlib/eventbus/eventbus_test.v create mode 100644 vlib/eventbus/params.v diff --git a/vlib/eventbus/eventbus.v b/vlib/eventbus/eventbus.v new file mode 100644 index 0000000000..95e82ac6ea --- /dev/null +++ b/vlib/eventbus/eventbus.v @@ -0,0 +1,116 @@ +module eventbus + +pub struct Subscriber { + mut: + registry &Registry +} +struct Registry{ + mut: + names []string + events []voidptr + once []string +} + +struct EventHandler { + func fn(Params) +} + +pub struct EventBus{ + mut: + registry &Registry + pub: + subscriber Subscriber +} + +// EventBus Methods + +pub fn new() &EventBus{ + registry := &Registry{ + names: [] + events: [] + once: [] + } + return &EventBus{ + registry, + Subscriber{registry} + } +} + +pub fn (eb mut EventBus) publish(name string, p Params){ + for i, n in eb.registry.names { + if name == n { + eh := eb.registry.events[i] + invoke(eh, p) + once_index := eb.registry.once.index(eb.registry.names[i]) + if once_index > -1 { + eb.registry.events.delete(i) + eb.registry.names.delete(i) + eb.registry.once.delete(once_index) + } + } + } +} + +pub fn (eb mut EventBus) clear_all(){ + for i, n in eb.registry.names { + eb.registry.delete_entry(i) + } +} + +pub fn (eb &EventBus) has_subscriber(name string) bool { + return eb.registry.check_subscriber(name) +} + +// Subscriber Methods + +pub fn (s mut Subscriber) subscribe(name string, handler fn(Params)){ + s.registry.names << name + v := voidptr(handler) + s.registry.events << v +} + +pub fn (s mut Subscriber) subscribe_once(name string, handler fn(Params)){ + s.subscribe(name, handler) + s.registry.once << name +} + +pub fn (s &Subscriber) is_subscribed(name string) bool { + return s.registry.check_subscriber(name) +} + +pub fn (s mut Subscriber) unsubscribe(name string, handler fn(Params)){ + v := voidptr(handler) + for i, n in s.registry.names { + if name == n { + eh := s.registry.events[i] + if eh == v { + s.registry.delete_entry(i) + } + } + } +} + +// Registry Methods + +fn (r &Registry) check_subscriber(name string) bool { + for n in r.names { + if name == n {return true} + } + return false +} + +fn (r mut Registry) delete_entry(index int) { + once_index := r.once.index(r.names[index]) + if once_index > -1 { + r.once.delete(once_index) + } + r.events.delete(index) + r.names.delete(index) +} + +// Helper Functions + +fn invoke(p voidptr, arr Params){ + handler := EventHandler{p}.func + handler(arr) +} \ No newline at end of file diff --git a/vlib/eventbus/eventbus_test.v b/vlib/eventbus/eventbus_test.v new file mode 100644 index 0000000000..92cf9b0712 --- /dev/null +++ b/vlib/eventbus/eventbus_test.v @@ -0,0 +1,42 @@ +import ( + eventbus +) + +fn test_eventbus(){ + mut eb := eventbus.new() + eb.subscriber.subscribe_once("on_test", on_test) + assert eb.has_subscriber("on_test") == true + assert eb.subscriber.is_subscribed("on_test") == true + mut params := eventbus.Params{} + params.put_string("eventbus", "vevent") + eb.publish("on_test", params) + assert eb.has_subscriber("on_test") == false + assert eb.subscriber.is_subscribed("on_test") == false + eb.subscriber.subscribe_once("on_test", on_test) + assert eb.has_subscriber("on_test") == true + assert eb.subscriber.is_subscribed("on_test") == true + eb.clear_all() + assert eb.has_subscriber("on_test") == false + assert eb.subscriber.is_subscribed("on_test") == false +} + +fn test_params(){ + mut params := eventbus.Params{} + params.put_string("string", "vevent") + params.put_int("int", 20) + params.put_bool("bo", true) + eventbus.put_array(mut params, "array", [1,2,3]) + eventbus.put_map(mut params, "map", "", {"hello": "world"}) + + assert params.get_string("string") == "vevent" + assert params.get_int("int") == 20 + assert params.get_bool("bo") == true + arr := eventbus.get_array(params, "array", 0) + assert arr[0] == 1 + m := params.get_string_map("map") + assert m["hello"] == "world" +} + +fn on_test(p eventbus.Params) { + assert p.get_string("eventbus") == "vevent" +} \ No newline at end of file diff --git a/vlib/eventbus/params.v b/vlib/eventbus/params.v new file mode 100644 index 0000000000..7a1eff9d18 --- /dev/null +++ b/vlib/eventbus/params.v @@ -0,0 +1,127 @@ +module eventbus + +/* +NOTE: All these non-generic methods are temporary until +V has a properly functioning generic system +*/ + +pub struct Params { + mut: + params []Param +} + +struct Param{ + typ string + name string + value voidptr + keys voidptr +} + +pub fn (p Params) get_string(name string) string { + param, is_type := p.get_param(name, "string") + return if is_type {string(byteptr(param.value))}else{""} +} + +pub fn (p Params) get_int(name string) int { + param, is_type := p.get_param(name, "num") + return if is_type {int(param.value)}else{0} +} + +pub fn (p Params) get_bool(name string) bool { + param, is_type := p.get_param(name, "bool") + return if is_type {int(param.value) == 1}else{false} +} + +pub fn get_array(p Params, name string, def T) []T { + param, _ := p.get_param(name, "") + if param.typ.contains("[") { + len := parse_len(param.typ, "[", "]") + mut b := []T + b = C.new_array_from_c_array_no_alloc(len, len, sizeof(T), param.value) + return b + } + return [] +} + +// TODO: make this a method after generics are fixed. +pub fn get_map(p Params, name string, valueTyp T) map[string]T { + param, _ := p.get_param(name, "") + ret := map[string]T + if param.typ.contains("map(") { + len := parse_len(param.typ, "(", ")") + mut keys := []string + // the best way (that I could find) to convert voidptr into array without alloc + // since we know that the voidptr we are getting is an array + keys = C.new_array_from_c_array_no_alloc(len, len, sizeof(T), param.keys) + for i, key in keys { + //the most simple way to set map value without knowing the typ + C.map_set(&ret, key, param.value + i * sizeof(T)) + } + } + return ret +} + +pub fn (p Params) get_string_map(name string) map[string]string { + return get_map(p, name, "") +} + +pub fn (p Params) get_int_map(name string) map[string]int { + return get_map(p, name, 0) +} + +pub fn (p Params) get_bool_map(name string) map[string]bool { + return get_map(p, name, false) +} + +// TODO: make this a method after generics are fixed. +pub fn put_map(p mut Params, name string, valueTyp T, value map[string]T) { + keys := value.keys() + mut vals := []T + for key in keys { + vals << value[key] + } + p.params << Param { + typ: "map($value.size)" + name: name + keys: keys.data + value: vals.data + } +} + +// TODO: make this a method after generic methods are working. +pub fn put_array(p mut Params, name string, arr []T) { + p.put_custom(name, "[$arr.len]", arr.data) +} + +pub fn (p mut Params) put_int(name string, num int) { + p.put_custom(name, "num", num) +} + +pub fn (p mut Params) put_string(name string, s string) { + p.put_custom(name, "string", s.str) +} + +pub fn (p mut Params) put_bool(name string, val bool) { + p.put_custom(name, "bool", int(val)) +} + +pub fn (p mut Params) put_custom(name string, typ string, data voidptr) { + p.params << Param {typ, name, data, voidptr(0)} +} + +//HELPERS + +fn parse_len(typ, s_tok, e_tok string) int { + len := typ.substr(typ.index(s_tok) + 1, typ.index(e_tok)).int() + //t := typ.substr(typ.index(e_tok) + 1, typ.len) + return len +} + +fn (p Params) get_param(name string, typ string) (Param, bool) { + for param in p.params { + if param.name == name { + return param, param.typ == typ + } + } + return Param{value: voidptr(0), keys: voidptr(0)}, false +} \ No newline at end of file