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

Message.pudu

Pudu83 lines3.3 KB

GitHub ↗
1/** @Mediator.Message.Vocabulary — what every kind of message shares */2module PuduLangMediator.Message34import PuduLangMediator as Messaging5import PuduLangMediator.Utils.Erasure as Erasure67/** @Mediator.Message.Shape — a request with or without a response, a notification, or a stream */8export type Shape = Query | Command | Event | Sequence910/** @Mediator.Message.Info — the name, shape, and tags of one kind */11export type Info = { name: Str, shape: Shape, tags: Array[Str] }1213/** @Mediator.Message.Envelope — a message whose type the sender does not name */14export type Envelope = { info: Info, packed: Erasure.Packed, summary: Str }1516/** @Mediator.Message.Fault — why an envelope reached no handler */17export type Fault = NoRoute(Str) | WrongKind(Str)1819/** @Mediator.Message.Delivery — the outcome of an envelope, why there is none, or no listener */20export type Delivery = Delivered(Erasure.Packed) | Undelivered(Fault) | Unheard2122/** @Mediator.Message.Answer — what an envelope's handler answered */23export type Answer = { info: Info, delivery: Delivery, failed: Bool, summary: Str }2425/// Whether the kind carries `tag`.26export fn hasTag(info: &Info, tag: Str) -> Bool { info.tags.contains(tag) }2728/// The shape's name.29export fn shapeName(shape: &Shape) -> Str {30  match shape {31    case Query => "query"32    case Command => "command"33    case Event => "event"34    case Sequence => "sequence"35  }36}3738/// The failure an undelivered envelope answers, for any error type.39export fn failureOf[E](fault: &Fault) -> Messaging.Failure[E] {40  match fault {41    case NoRoute(name) => Messaging.Unhandled(name)42    case WrongKind(name) => Messaging.Mismatched(name)43  }44}4546/// The answer holding `outcome`, readable back only through `outcomes`.47export fn answered[T, E](info: &Info, outcomes: &Erasure.Slot[Messaging.Outcome[T, E]], outcome: Messaging.Outcome[T, E]) -> Answer {48  let failed = match outcome {49    case Ok(_) => false50    case Err(_) => true51  }52  let summary = Messaging.summarize(&outcome)53  Answer{info: *info, delivery: Delivered(Erasure.pack(outcomes, outcome)), failed: failed, summary: summary}54}5556/// The answer of an envelope that reached no handler.57export fn undelivered(info: &Info, fault: Fault) -> Answer {58  let failure: Messaging.Failure[Str] = failureOf(&fault)59  Answer{info: *info, delivery: Undelivered(fault), failed: true, summary: Messaging.describe(&failure)}60}6162/// The answer of a notification envelope whose kind has no handler: a publication nobody heard.63export fn unheard(info: &Info) -> Answer {64  let quiet: Messaging.Outcome[(), Str] = Ok(())65  Answer{info: *info, delivery: Unheard, failed: false, summary: Messaging.summarize(&quiet)}66}6768/// The outcome an answer holds, read through `outcomes`; `None` when another kind answered it or69/// nobody heard it.70export fn read[T, E](outcomes: &Erasure.Slot[Messaging.Outcome[T, E]], answer: &Answer) -> Option[Messaging.Outcome[T, E]] {71  match answer.delivery {72    case Delivered(packed) => Erasure.unpack(outcomes, &packed)73    case Undelivered(fault) => Some(Err(failureOf(&fault)))74    case Unheard => None75  }76}7778/// The envelope carrying `message`, readable back only through `messages`.79export fn sealed[M](info: &Info, messages: &Erasure.Slot[M], message: M) -> Envelope {80  let summary = show(message)81  Envelope{info: *info, packed: Erasure.pack(messages, message), summary: summary}82}83