///|
priv enum OpHandler {
Sync((Json) -> Json raise OpDispatchError)
Async(async (Json) -> Json)
WithContext(async (AppCommandRequestContext, Json) -> Json)
}
///|
priv struct OpRegistry {
handlers : Map[String, OpHandler]
}
///|
fn OpRegistry::OpRegistry() -> OpRegistry {
{ handlers: {}, }
}
///|
/// Registers a typed op handler that cannot fail explicitly.
fn[Payload : @json.FromJson, Reply : ToJson] OpRegistry::handle(
self : OpRegistry,
name : String,
callback : (Payload) -> Reply raise,
) -> Unit raise OpRegistrationError {
validate_op_name(name)
guard !self.handlers.contains(name) else { raise DuplicateName(name~) }
self.handlers.set(
name,
Sync(fn(payload_json) raise OpDispatchError {
let payload : Payload = @json.from_json(payload_json) catch {
err => raise InvalidPayload(name~, detail=err.to_string())
}
try callback(payload) catch {
err => raise HandlerFailed(name~, detail=@debug.render(Repr(err)))
} noraise {
reply => ToJson::to_json(reply)
}
}),
)
}
///|
/// Registers a typed async op handler.
fn[Payload : @json.FromJson, Reply : ToJson] OpRegistry::handle_async(
self : OpRegistry,
name : String,
callback : async (Payload) -> Reply,
) -> Unit raise OpRegistrationError {
validate_op_name(name)
guard !self.handlers.contains(name) else { raise DuplicateName(name~) }
self.handlers.set(
name,
Async(async fn(payload_json) {
let payload : Payload = @json.from_json(payload_json) catch {
err => raise InvalidPayload(name~, detail=err.to_string())
}
try callback(payload) catch {
err => raise HandlerFailed(name~, detail=@debug.render(Repr(err)))
} noraise {
reply => ToJson::to_json(reply)
}
}),
)
}
///|
/// Registers a typed async op handler that receives request-scoped context.
fn[Payload : @json.FromJson, Reply : ToJson] OpRegistry::handle_async_with_context(
self : OpRegistry,
name : String,
callback : async (AppCommandRequestContext, Payload) -> Reply,
) -> Unit raise OpRegistrationError {
validate_op_name(name)
guard !self.handlers.contains(name) else { raise DuplicateName(name~) }
self.handlers.set(
name,
WithContext(async fn(context, payload_json) {
let payload : Payload = @json.from_json(payload_json) catch {
err => raise InvalidPayload(name~, detail=err.to_string())
}
try callback(context, payload) catch {
err => raise HandlerFailed(name~, detail=@debug.render(Repr(err)))
} noraise {
reply => ToJson::to_json(reply)
}
}),
)
}
///|
async fn OpRegistry::call(
self : OpRegistry,
name : String,
payload : Json,
context : AppCommandRequestContext?,
) -> Json {
match self.handlers.get(name) {
Some(Sync(handler)) => handler(payload)
Some(Async(handler)) => handler(payload)
Some(WithContext(handler)) =>
match context {
Some(context) => handler(context, payload)
None => raise RequestContextRequired(name~)
}
None => raise UnknownOp(name~)
}
}
///|
fn validate_op_name(name : String) -> Unit raise OpRegistrationError {
guard name.trim().to_owned() != "" else { raise EmptyName }
}