///|
/// Context passed to handlers and middleware.
pub(all) struct EventContext {
bus_name : String
publish_index : Int
handler_index : Int
} derive(Eq, Debug)
///|
pub(all) enum HandlerOutcome {
OutcomeHandled(String)
OutcomeIgnored(String)
OutcomeFailed(String)
OutcomeStopped(String)
} derive(Eq, Debug)
///|
pub(all) enum MiddlewareOutcome {
MiddlewareContinue(Event)
MiddlewareHalt(String)
} derive(Debug)
///|
pub type HandlerFn = (Event, EventContext) -> HandlerOutcome
///|
pub type MiddlewareFn = (Event, EventContext) -> MiddlewareOutcome
///|
pub(all) struct Handler {
id : Int
name : String
subscription : Subscription
priority : Int
callback : HandlerFn
}
///|
pub(all) struct Middleware {
name : String
callback : MiddlewareFn
}
///|
pub(all) struct EventBus {
name : String
handlers : Array[Handler]
middlewares : Array[Middleware]
scheduled : Array[ScheduledEvent]
trace : Array[TraceEntry]
publish_count : Int
next_id : Int
}
///|
pub fn Handler::new(
id : Int,
name : String,
subscription : Subscription,
priority : Int,
callback : HandlerFn,
) -> Handler {
{ id, name, priority, subscription, callback }
}
///|
pub fn Middleware::new(name : String, callback : MiddlewareFn) -> Middleware {
{ name, callback }
}
///|
pub fn EventBus::new(name? : String = "default") -> EventBus {
{
name,
handlers: [],
middlewares: [],
scheduled: [],
trace: [],
publish_count: 0,
next_id: 1,
}
}
///|
pub fn EventBus::handler_count(self : EventBus) -> Int {
self.handlers.length()
}
///|
pub fn EventBus::middleware_count(self : EventBus) -> Int {
self.middlewares.length()
}
///|
pub fn EventBus::scheduled_count(self : EventBus) -> Int {
self.scheduled.length()
}
///|
pub fn EventBus::trace_entries(self : EventBus) -> Array[TraceEntry] {
self.trace
}
///|
pub fn EventBus::with_handler(self : EventBus, handler : Handler) -> EventBus {
let handlers : Array[Handler] = []
for existing in self.handlers {
handlers.push(existing)
}
handlers.push(handler)
{ ..self, handlers, next_id: self.next_id + 1 }
}
///|
pub fn EventBus::subscribe(
self : EventBus,
name : String,
topic? : String = "",
tag? : String = "",
min_priority? : Int = 0,
once? : Bool = false,
callback : HandlerFn,
handler_priority? : Int = 0,
) -> EventBus {
let sub = Subscription::new(
self.next_id,
name,
topic~,
tag~,
min_priority~,
once~,
)
self.with_handler(
Handler::new(self.next_id, name, sub, handler_priority, callback),
)
}
///|
fn copy_handler_with_active(handler : Handler, active : Bool) -> Handler {
{ ..handler, subscription: { ..handler.subscription, active, } }
}
///|
pub fn EventBus::pause(self : EventBus, id : Int) -> EventBus {
let handlers : Array[Handler] = []
for handler in self.handlers {
if handler.id == id {
handlers.push(copy_handler_with_active(handler, false))
} else {
handlers.push(handler)
}
}
{ ..self, handlers, }
}
///|
pub fn EventBus::resume_handler(self : EventBus, id : Int) -> EventBus {
let handlers : Array[Handler] = []
for handler in self.handlers {
if handler.id == id {
handlers.push(copy_handler_with_active(handler, true))
} else {
handlers.push(handler)
}
}
{ ..self, handlers, }
}
///|
pub fn EventBus::unsubscribe(self : EventBus, id : Int) -> EventBus {
let handlers : Array[Handler] = []
for handler in self.handlers {
if handler.id != id {
handlers.push(handler)
}
}
{ ..self, handlers, }
}
///|
pub fn EventBus::has_handler(self : EventBus, id : Int) -> Bool {
for handler in self.handlers {
if handler.id == id {
return true
}
}
false
}
///|
pub fn EventBus::active_handler_count(self : EventBus) -> Int {
let mut count = 0
for handler in self.handlers {
if handler.subscription.active {
count = count + 1
}
}
count
}
///|
fn insert_handler_by_priority(
ordered : Array[Handler],
handler : Handler,
) -> Array[Handler] {
let result : Array[Handler] = []
let mut inserted = false
for existing in ordered {
if !inserted && handler.priority > existing.priority {
result.push(handler)
inserted = true
}
result.push(existing)
}
if !inserted {
result.push(handler)
}
result
}
///|
fn ordered_handlers(handlers : Array[Handler]) -> Array[Handler] {
let mut result : Array[Handler] = []
for handler in handlers {
result = insert_handler_by_priority(result, handler)
}
result
}
///|
pub fn EventBus::use_middleware(
self : EventBus,
name : String,
callback : MiddlewareFn,
) -> EventBus {
let middlewares : Array[Middleware] = []
for existing in self.middlewares {
middlewares.push(existing)
}
middlewares.push(Middleware::new(name, callback))
{ ..self, middlewares, }
}
///|
fn EventBus::append_trace(
self : EventBus,
kind : TraceKind,
event_name : String,
actor : String,
message : String,
) -> EventBus {
let trace : Array[TraceEntry] = []
for existing in self.trace {
trace.push(existing)
}
trace.push(TraceEntry::new(trace.length(), kind, event_name, actor, message))
{ ..self, trace, }
}
///|
fn outcome_to_report(
handler : String,
outcome : HandlerOutcome,
) -> HandlerReport {
match outcome {
OutcomeHandled(note) => HandlerReport::ok(handler, note~)
OutcomeIgnored(note) => HandlerReport::ignored(handler, note~)
OutcomeFailed(reason) => HandlerReport::failed(handler, reason)
OutcomeStopped(note) => HandlerReport::ok(handler, note~)
}
}
///|
fn outcome_stops(outcome : HandlerOutcome) -> Bool {
match outcome {
OutcomeStopped(_) => true
_ => false
}
}
///|
fn run_middlewares(
bus : EventBus,
event : Event,
context : EventContext,
) -> (EventBus, Event?, Array[HandlerReport]) {
let errors : Array[HandlerReport] = []
let mut current = event
let mut next_bus = bus
let mut halted = false
for middleware in bus.middlewares {
if halted {
()
} else {
next_bus = next_bus.append_trace(
TraceMiddleware,
current.name,
middleware.name,
"middleware start",
)
match (middleware.callback)(current, context) {
MiddlewareContinue(updated) => current = updated
MiddlewareHalt(reason) => {
halted = true
errors.push(HandlerReport::failed(middleware.name, reason))
next_bus = next_bus.append_trace(
TraceError,
current.name,
middleware.name,
reason,
)
}
}
}
}
if halted {
(next_bus, None, errors)
} else {
(next_bus, Some(current), errors)
}
}
///|
pub fn EventBus::publish(
self : EventBus,
event : Event,
) -> (EventBus, DispatchReport) {
let context = {
bus_name: self.name,
publish_index: self.publish_count,
handler_index: 0,
}
let mut next_bus = self.append_trace(
TracePublish,
event.name,
self.name,
"publish start",
)
let (after_middleware, maybe_event, middleware_reports) = run_middlewares(
next_bus, event, context,
)
next_bus = after_middleware
let reports : Array[HandlerReport] = []
for item in middleware_reports {
reports.push(item)
}
let mut stopped = false
let once_ids : Array[Int] = []
match maybe_event {
None => stopped = true
Some(current) => {
let mut handler_index = 0
for handler in ordered_handlers(next_bus.handlers) {
if stopped {
()
} else if handler.subscription.matches(current) {
let handler_context = {
bus_name: self.name,
publish_index: self.publish_count,
handler_index,
}
next_bus = next_bus.append_trace(
TraceHandler,
current.name,
handler.name,
"handler start",
)
let outcome = (handler.callback)(current, handler_context)
reports.push(outcome_to_report(handler.name, outcome))
if handler.subscription.once {
once_ids.push(handler.id)
}
if outcome_stops(outcome) {
stopped = true
}
} else {
reports.push(HandlerReport::ignored(handler.name, note="filtered"))
}
handler_index = handler_index + 1
}
}
}
next_bus = { ..next_bus, publish_count: next_bus.publish_count + 1 }
for id in once_ids {
next_bus = next_bus.unsubscribe(id)
}
(next_bus, DispatchReport::from_reports(event, reports, stopped~))
}
///|
pub fn EventBus::publish_many(
self : EventBus,
events : Array[Event],
) -> (EventBus, Array[DispatchReport]) {
let reports : Array[DispatchReport] = []
let mut bus = self
for event in events {
let (next, report) = bus.publish(event)
bus = next
reports.push(report)
}
(bus, reports)
}