Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-mediator

In-process messaging for Pudu: requests, notifications, streams, pipeline behaviors, processors, and exception handling

0.1.0Apache-2.01

InstallClose

Request.pudu

Pudu76 lines3.6 KB

GitHub ↗
1/** @Mediator.Request.Kind — requests answered by exactly one handler */2module PuduLangMediator.Request34import PuduLangMediator.Context as Context5import PuduLangMediator.Message as Message6import PuduLangMediator as Messaging7import PuduLangMediator.Utils.Erasure as Erasure89/** @Mediator.Request.Handler — answers one request */10export type Handler[Q, R, E] = fn(Q, Context.Context) -> Messaging.Outcome[R, E]1112/** @Mediator.Request.Next — the rest of the pipeline, given the context to run it with */13export type Next[R, E] = fn(Context.Context) -> Messaging.Outcome[R, E]1415/** @Mediator.Request.Behavior — wraps the rest of the pipeline for one kind */16export type Behavior[Q, R, E] = fn(Q, Context.Context, Next[R, E]) -> Messaging.Outcome[R, E]1718/** @Mediator.Request.PreProcessor — runs before the handler; a failure stops the request */19export type PreProcessor[Q, E] = fn(Q, Context.Context) -> Messaging.Outcome[(), E]2021/** @Mediator.Request.PostProcessor — runs after a handler that answered a value */22export type PostProcessor[Q, R, E] = fn(Q, R, Context.Context) -> Messaging.Outcome[(), E]2324/** @Mediator.Request.ExceptionHandler — may answer a value in place of a failure */25export type ExceptionHandler[Q, R, E] = fn(Q, Messaging.Failure[E], Context.Context) -> Option[R]2627/** @Mediator.Request.ExceptionAction — observes a failure that keeps propagating */28export type ExceptionAction[Q, E] = fn(Q, Messaging.Failure[E], Context.Context) -> ()2930/** @Mediator.Request.Route — every component registered for one kind */31export type Route[Q, R, E] = {32  handlers: Array[Handler[Q, R, E]],33  behaviors: Array[Behavior[Q, R, E]],34  preProcessors: Array[PreProcessor[Q, E]],35  postProcessors: Array[PostProcessor[Q, R, E]],36  exceptionHandlers: Array[ExceptionHandler[Q, R, E]],37  exceptionActions: Array[ExceptionAction[Q, E]]38}3940/** @Mediator.Request.Kind — the typed identity of one request */41export type Kind[Q, R, E] = {42  info: Message.Info,43  routes: Erasure.Slot[Route[Q, R, E]],44  invokers: Erasure.Slot[Handler[Q, R, E]],45  requests: Erasure.Slot[Q],46  outcomes: Erasure.Slot[Messaging.Outcome[R, E]]47}4849/// A kind of request answering `R` or failing with `E`. Make each kind once and share it: two50/// kinds made by separate calls are different kinds, even under one name.51export fn kind[Q, R, E](name: Str) -> Kind[Q, R, E] { made(name, Message.Query) }5253/// A kind of request answering no value.54export fn command[Q, E](name: Str) -> Kind[Q, (), E] { made(name, Message.Command) }5556/// A kind with fresh slots under `name` and `shape`.57fn made[Q, R, E](name: Str, shape: Message.Shape) -> Kind[Q, R, E] {58  Kind{info: Message.Info{name: name, shape: shape, tags: []}, routes: Erasure.slot(), invokers: Erasure.slot(), requests: Erasure.slot(), outcomes: Erasure.slot()}59}6061/// The same kind carrying `tags` as well, which open components may read.62export fn tagged[Q, R, E](subject: &Kind[Q, R, E], tags: Array[Str]) -> Kind[Q, R, E] {63  Kind{..*subject, info: Message.Info{..subject.info, tags: subject.info.tags.concat(tags)}}64}6566/// A route with no components.67export fn route[Q, R, E]() -> Route[Q, R, E] {68  Route{handlers: [], behaviors: [], preProcessors: [], postProcessors: [], exceptionHandlers: [], exceptionActions: []}69}7071/// An envelope carrying `request`, for a sender that does not name its type.72export fn envelope[Q, R, E](subject: &Kind[Q, R, E], request: Q) -> Message.Envelope { Message.sealed(&subject.info, &subject.requests, request) }7374/// The outcome an answer holds, when this kind answered it.75export fn answerOf[Q, R, E](subject: &Kind[Q, R, E], answer: &Message.Answer) -> Option[Messaging.Outcome[R, E]] { Message.read(&subject.outcomes, answer) }76