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

Catalog.pudu

Pudu141 lines5.1 KB

GitHub ↗
1/** @Mediator.Catalog.Registry — every registration gathered before a build */2module PuduLangMediator.Catalog34import Std.HashMap as HashMap5import PuduLangMediator.Context as Context6import PuduLangMediator.Domain.Census as Census7import PuduLangMediator.Domain.Order as Order8import PuduLangMediator.Message as Message9import PuduLangMediator.Open as Open10import PuduLangMediator.Publishing as Publishing11import PuduLangMediator.Utils.Erasure as Erasure1213/** @Mediator.Catalog.Scope — which failures exception actions observe */14export type Scope = ForUnhandled | ForAll1516/** @Mediator.Catalog.Assembly — the open components and options every kind is built with */17export type Assembly = {18  marks: Array[Order.Mark],19  behaviors: Array[dynamic Open.Behavior],20  preProcessors: Array[dynamic Open.PreProcessor],21  postProcessors: Array[dynamic Open.PostProcessor],22  exceptionActions: Array[dynamic Open.ExceptionAction],23  listeners: Array[Open.Listener],24  streamBehaviors: Array[dynamic Open.StreamBehavior],25  publisher: dynamic Publishing.Strategy,26  actionScope: Scope27}2829/** @Mediator.Catalog.Dispatch — how an envelope of one kind is answered */30export type Dispatch31  = Answering(fn(Message.Envelope, Context.Context) -> Message.Answer)32  | Streaming(fn(Message.Envelope, Context.Context, fn(Erasure.Packed) -> Bool) -> Message.Answer)3334/** @Mediator.Catalog.Compiled — one kind's composed pipeline, typed and untyped */35export type Compiled = { info: Message.Info, invoke: Erasure.Packed, dispatch: Dispatch }3637/** @Mediator.Catalog.Entry — one kind's registrations, counted, before composition */38export type Entry = {39  info: Message.Info,40  route: Erasure.Packed,41  handlers: Int,42  components: Int,43  collisions: Int,44  finish: fn(Erasure.Packed, Assembly) -> Compiled45}4647/** @Mediator.Catalog.Catalog — the registrations of one mediator, by shape and name */48export type Catalog = {49  requests: HashMap.HashMap[Str, Entry],50  notifications: HashMap.HashMap[Str, Entry],51  streams: HashMap.HashMap[Str, Entry],52  assembly: Assembly53}5455/// A catalog with no registrations, publishing with `publisher` and scoping actions by `actionScope`.56export fn empty(publisher: dynamic Publishing.Strategy, actionScope: Scope) -> Catalog {57  Catalog {58    requests: HashMap.empty(),59    notifications: HashMap.empty(),60    streams: HashMap.empty(),61    assembly: Assembly {62      marks: [],63      behaviors: [],64      preProcessors: [],65      postProcessors: [],66      exceptionActions: [],67      listeners: [],68      streamBehaviors: [],69      publisher: publisher,70      actionScope: actionScope71    }72  }73}7475/// The table with `change` applied to the route of the kind `info` names, counting the handlers76/// and components it adds. A first registration starts from `blank`; a registration whose route77/// does not read back through `routes` belongs to another kind of the same name and is counted78/// as a collision instead.79export fn touch[R](80  table: &HashMap.HashMap[Str, Entry],81  info: &Message.Info,82  routes: &Erasure.Slot[R],83  blank: R,84  change: fn(R) -> R,85  handlers: Int,86  components: Int,87  finish: fn(Erasure.Packed, Assembly) -> Compiled88) -> HashMap.HashMap[Str, Entry] {89  let name = info.name90  match HashMap.get(table, &name) {91    case None => {92      HashMap.insert(table, name, Entry{info: *info, route: Erasure.pack(routes, change(blank)), handlers: handlers, components: components, collisions: 0, finish: finish})93    }94    case Some(entry) => {95      match Erasure.unpack(routes, &entry.route) {96        case Some(route) => {97          HashMap.insert(table, name, Entry {98              ..entry,99              route: Erasure.pack(routes, change(route)),100              handlers: entry.handlers + handlers,101              components: entry.components + components102            })103        }104        case None => HashMap.insert(table, name, Entry{..entry, collisions: entry.collisions + 1})105      }106    }107  }108}109110/// The catalog with one more registration of `layer` for the kind `name`, in order.111export fn marked(catalog: Catalog, layer: Order.Layer, name: Str) -> Catalog {112  Catalog{..catalog, assembly: Assembly{..catalog.assembly, marks: catalog.assembly.marks.push(Order.Closed(layer, name))}}113}114115/// Every problem with the catalog's registrations, requests first, then notifications, then streams.116export fn problems(catalog: &Catalog) -> Array[Str] {117  var found: Array[Str] = []118  for table in [catalog.requests, catalog.notifications, catalog.streams] {119    for entry in HashMap.values(&table) {120      let single = entry.info.shape != Message.Event121      found = found.concat(Census.problems(&Census.Census {122            name: entry.info.name,123            handlers: entry.handlers,124            components: entry.components,125            collisions: entry.collisions,126            single: single127          }))128    }129  }130  found131}132133/// Every entry of a table composed with the catalog's assembly.134export fn compile(table: &HashMap.HashMap[Str, Entry], assembly: &Assembly) -> HashMap.HashMap[Str, Compiled] {135  var compiled: HashMap.HashMap[Str, Compiled] = HashMap.empty()136  for entry in HashMap.values(table) {137    compiled = HashMap.insert(&compiled, entry.info.name, (entry.finish)(entry.route, *assembly))138  }139  compiled140}141