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

Context.pudu

Pudu91 lines3.9 KB

GitHub ↗
1/** @Mediator.Context.Carrier — per-message cancellation token and typed items */2module PuduLangMediator.Context34import Std.Concurrent.Cancel as Cancel5import Std.HashMap as HashMap6import PuduLangMediator as Messaging7import PuduLangMediator.Utils.Erasure as Erasure8import PuduLangMediator.Utils.Shared as Shared910/** @Mediator.Context.Context — what one message carries through its pipeline */11export type Context = { token: Cancel.Token, items: Shared.Shared[HashMap.HashMap[Str, Erasure.Packed]] }1213/** @Mediator.Context.Key — a typed name for one item */14export type Key[T] = { name: Str, slot: Erasure.Slot[T] }1516/// A context with a token that fires only when cancelled, and no items.17export fn create() -> Context { Context{token: Cancel.token(), items: Shared.shared(HashMap.empty())} }1819/// A fresh context whose message stops when `token` fires.20export fn cancellable(token: Cancel.Token) -> Context { Context{..create(), token: token} }2122/// The same context observing another token; items stay shared with the original.23export fn withToken(source: &Context, token: Cancel.Token) -> Context { Context{..*source, token: token} }2425/// The same context with a token that fires when the original's does, after `millis`, or when26/// cancelled itself; items stay shared.27export fn expiring(source: &Context, millis: Int) -> Context { withToken(source, Cancel.childExpiring(&source.token, millis)) }2829/// A key whose items read back as `T`. Two keys made by separate calls never read each other's30/// items, even under one name; make each key once and share it.31export fn key[T](name: Str) -> Key[T] { Key{name: name, slot: Erasure.slot()} }3233/// Stores an item, replacing any item under the same name.34export fn set[T](target: &Context, name: &Key[T], value: T) -> () {35  let packed = Erasure.pack(&name.slot, value)36  let field = name.name37  Shared.update(&target.items, fn(held: HashMap.HashMap[Str, Erasure.Packed]) -> HashMap.HashMap[Str, Erasure.Packed] {38      HashMap.insert(&held, field, packed)39    })40}4142/// The item under a key, when it is present and was stored through the same key.43export fn get[T](source: &Context, name: &Key[T]) -> Option[T] {44  let packed = HashMap.get(&Shared.current(&source.items), &name.name) ?45  Erasure.unpack(&name.slot, &packed)46}4748/// The item under a key, or `fallback` when it is absent.49export fn getOr[T](source: &Context, name: &Key[T], fallback: T) -> T {50  match get(source, name) {51    case Some(found) => found52    case None => fallback53  }54}5556/// Whether an item is stored under the key's name.57export fn has[T](source: &Context, name: &Key[T]) -> Bool { HashMap.containsKey(&Shared.current(&source.items), &name.name) }5859/// Removes the item under a key.60export fn remove[T](target: &Context, name: &Key[T]) -> () {61  let field = name.name62  Shared.update(&target.items, fn(held: HashMap.HashMap[Str, Erasure.Packed]) -> HashMap.HashMap[Str, Erasure.Packed] {63      HashMap.remove(&held, &field)64    })65}6667/// The names of every stored item, in the order they were first stored.68export fn names(source: &Context) -> Array[Str] { HashMap.keys(&Shared.current(&source.items)) }6970/// Asks the message holding this context to stop. The first reason given is kept.71export fn cancel(target: &Context, why: Str) -> () { let _cancelled = Cancel.cancel(&target.token, why) }7273/// Whether the context's token has fired.74export fn stopped(source: &Context) -> Bool { Cancel.stopped(&source.token) }7576/// `Ok` while the token has not fired, and `Cancelled` once it has, so a handler stops with `?`.77export fn check[E](source: &Context) -> Result[(), Messaging.Failure[E]] {78  match Cancel.check(&source.token) {79    case Ok(_) => Ok(())80    case Err(why) => Err(Messaging.Cancelled(Cancel.explain(&why)))81  }82}8384/// Sleeps `millis`, or answers `Cancelled` as soon as the token fires.85export fn pause[E](source: &Context, millis: Int) -> Result[(), Messaging.Failure[E]] {86  match Cancel.pause(&source.token, millis) {87    case Ok(_) => Ok(())88    case Err(why) => Err(Messaging.Cancelled(Cancel.explain(&why)))89  }90}91