
Context.pudu
Pudu120 lines5.0 KB
1/** @Resilience.Context.Carrier — per-execution key, token, and properties */2module PuduLangResilience.Context34import Std.Concurrent.Cancel as Cancel5import Std.Decimal as Decimal6import Std.Map as Map7import Std.Option as Option8import PuduLangResilience as Resilience9import PuduLangResilience.Utils.Shared as Shared1011/** @Resilience.Context.Context — what one execution carries through a pipeline */12export type Context = {13 operationKey: Option[Str],14 token: Cancel.Token,15 properties: Shared.Shared[Map[Str, Str]]16}1718/** @Resilience.Context.Key — a typed name for one property */19export type Key[T] = { name: Str, encode: fn(T) -> Str, decode: fn(Str) -> Option[T] }202122export fn create() -> Context {23 Context{operationKey: None, token: Cancel.token(), properties: Shared.shared(Map.empty())}24}252627export fn keyed(operationKey: Str) -> Context { Context{..create(), operationKey: Some(operationKey)} }282930export fn cancellable(token: Cancel.Token) -> Context { Context{..create(), token: token} }313233export fn withToken(source: &Context, token: Cancel.Token) -> Context { Context{..*source, token: token} }343536export fn fork(source: &Context, token: Cancel.Token) -> Context {37 Context{..*source, token: token, properties: Shared.shared(Shared.current(&source.properties))}38}394041export fn adopt(target: &Context, source: &Context) -> () {42 let incoming = Shared.current(&source.properties)43 Shared.update(&target.properties, fn(held: Map[Str, Str]) -> Map[Str, Str] { Map.merge(&held, &incoming) })44}454647export fn key[T](name: Str, encode: fn(T) -> Str, decode: fn(Str) -> Option[T]) -> Key[T] {48 Key{name: name, encode: encode, decode: decode}49}505152export fn textKey(name: Str) -> Key[Str] {53 key(name, fn(value: Str) -> Str { value }, fn(text: Str) -> Option[Str] { Some(text) })54}555657export fn intKey(name: Str) -> Key[Int] {58 key(name, fn(value: Int) -> Str { show(value) }, fn(text: Str) -> Option[Int] {59 Option.andThen(Decimal.parse(text), fn(exact: Decimal) -> Option[Int] { Decimal.toInt(exact) })60 })61}626364export fn boolKey(name: Str) -> Key[Bool] {65 key(name, fn(value: Bool) -> Str { if value { "true" } else { "false" } }, fn(text: Str) -> Option[Bool] {66 if text == "true" { Some(true) } else if text == "false" { Some(false) } else { None }67 })68}697071export fn set[T](target: &Context, name: &Key[T], value: T) -> () {72 let text = (name.encode)(value)73 let field = name.name74 Shared.update(&target.properties, fn(held: Map[Str, Str]) -> Map[Str, Str] { Map.insert(&held, field, text) })75}767778export fn get[T](source: &Context, name: &Key[T]) -> Option[T] {79 Option.andThen(Map.get(&Shared.current(&source.properties), name.name), name.decode)80}818283export fn getOr[T](source: &Context, name: &Key[T], fallback: T) -> T { Option.unwrapOr(get(source, name), fallback) }848586export fn has[T](source: &Context, name: &Key[T]) -> Bool {87 Map.containsKey(&Shared.current(&source.properties), name.name)88}899091export fn remove[T](target: &Context, name: &Key[T]) -> () {92 let field = name.name93 Shared.update(&target.properties, fn(held: Map[Str, Str]) -> Map[Str, Str] { Map.remove(&held, field) })94}959697export fn names(source: &Context) -> Array[Str] { Map.keys(&Shared.current(&source.properties)) }9899100export fn cancel(target: &Context, why: Str) -> () { let _cancelled = Cancel.cancel(&target.token, why) }101102103export fn stopped(source: &Context) -> Bool { Cancel.stopped(&source.token) }104105106export fn check[E](source: &Context) -> Result[(), Resilience.Failure[E]] {107 match Cancel.check(&source.token) {108 case Ok(_) => Ok(())109 case Err(why) => Err(Resilience.Cancelled(Cancel.explain(&why)))110 }111}112113114export fn pause[E](source: &Context, millis: Int) -> Result[(), Resilience.Failure[E]] {115 match Cancel.pause(&source.token, millis) {116 case Ok(_) => Ok(())117 case Err(why) => Err(Resilience.Cancelled(Cancel.explain(&why)))118 }119}120