
Registry.pudu
Pudu114 lines5.4 KB
1/** @Resilience.Registry.Module — named pipelines built once and shared */2module PuduLangResilience.Registry34import Std.Map as Map5import PuduLangResilience.Constants.Messages as Messages6import PuduLangResilience.Pipeline as Pipeline7import PuduLangResilience.Strategy as Strategy8import PuduLangResilience.Utils.Shared as Shared9import PuduLangResilience.Utils.Template as Template1011/** @Resilience.Registry.BuilderContext — the key a pipeline is being built for */12export type BuilderContext = { key: Str, builderName: Str, instanceName: Option[Str] }1314/** @Resilience.Registry.Builder — the strategies of the pipeline for one key */15export type Builder[T, E] = fn(BuilderContext) -> Array[Strategy.Strategy[T, E]]1617/** @Resilience.Registry.Options — shared pipeline settings and how keys become names */18export type Options = {19 pipeline: Pipeline.Options,20 builderName: fn(Str) -> Str,21 instanceName: fn(Str) -> Option[Str]22}2324/** @Resilience.Registry.RegistryError — why a pipeline could not be answered */25export type RegistryError = NotFound(Str) | Invalid(Str, Pipeline.Invalid)2627/** @Resilience.Registry.Registry — builders and the pipelines built from them */28export type Registry[T, E] = {29 options: Options,30 entries: Shared.Shared[Map[Str, Entry[T, E]]]31}3233/** @Resilience.Registry.Entry — one key's builder and its current pipeline */34type Entry[T, E] = { builder: Builder[T, E], pipeline: Option[Pipeline.Pipeline[T, E]] }353637export fn defaults() -> Options {38 Options{pipeline: Pipeline.defaults(), builderName: fn(key: Str) -> Str { key }, instanceName: fn(_key: Str) -> Option[Str] { None }}39}404142export fn create[T, E]() -> Registry[T, E] { createWith(defaults()) }434445export fn createWith[T, E](options: Options) -> Registry[T, E] { Registry{options: options, entries: Shared.shared(Map.empty())} }464748export fn tryAddBuilder[T, E](registry: &Registry[T, E], key: Str, builder: Builder[T, E]) -> Bool {49 Shared.change(®istry.entries, fn(held: Map[Str, Entry[T, E]]) -> (Map[Str, Entry[T, E]], Bool) {50 if Map.containsKey(&held, key) { (held, false) } else { (Map.insert(&held, key, Entry{builder: builder, pipeline: None}), true) }51 })52}535455export fn get[T, E](registry: &Registry[T, E], key: Str) -> Result[Pipeline.Pipeline[T, E], RegistryError] {56 let options = registry.options57 Shared.change(®istry.entries, fn(held: Map[Str, Entry[T, E]]) -> (Map[Str, Entry[T, E]], Result[Pipeline.Pipeline[T, E], RegistryError]) {58 match Map.get(&held, key) {59 case None => (held, Err(NotFound(key)))60 case Some(entry) => resolve(&options, held, key, entry, false)61 }62 })63}646566export fn tryGet[T, E](registry: &Registry[T, E], key: Str) -> Option[Pipeline.Pipeline[T, E]] {67 match get(registry, key) {68 case Ok(pipeline) => Some(pipeline)69 case Err(_) => None70 }71}727374export fn getOrAdd[T, E](registry: &Registry[T, E], key: Str, builder: Builder[T, E]) -> Result[Pipeline.Pipeline[T, E], RegistryError] {75 let _added = tryAddBuilder(registry, key, builder)76 get(registry, key)77}78798081export fn reload[T, E](registry: &Registry[T, E], key: Str) -> Result[Pipeline.Pipeline[T, E], RegistryError] {82 let options = registry.options83 Shared.change(®istry.entries, fn(held: Map[Str, Entry[T, E]]) -> (Map[Str, Entry[T, E]], Result[Pipeline.Pipeline[T, E], RegistryError]) {84 match Map.get(&held, key) {85 case None => (held, Err(NotFound(key)))86 case Some(entry) => resolve(&options, held, key, entry, true)87 }88 })89}909192export fn keys[T, E](registry: &Registry[T, E]) -> Array[Str] { Map.keys(&Shared.current(®istry.entries)) }939495export fn explain(problem: &RegistryError) -> Str {96 match problem {97 case NotFound(key) => Template.fill(Messages.REGISTRY_NOT_FOUND, &[key])98 case Invalid(key, invalid) => Template.fill(Messages.REGISTRY_INVALID, &[key, invalid.problems.join(Messages.PROBLEM_SEPARATOR)])99 }100}101102103fn resolve[T, E](options: &Options, held: Map[Str, Entry[T, E]], key: Str, entry: Entry[T, E], rebuild: Bool) -> (Map[Str, Entry[T, E]], Result[Pipeline.Pipeline[T, E], RegistryError]) {104 if !rebuild {105 if let Some(existing) = entry.pipeline { return (held, Ok(existing)) }106 }107 let context = BuilderContext{key: key, builderName: (options.builderName)(key), instanceName: (options.instanceName)(key)}108 let settings = Pipeline.Options{..options.pipeline, name: Some(context.builderName), instance: context.instanceName}109 match Pipeline.buildWith(&settings, (entry.builder)(context)) {110 case Ok(built) => (Map.insert(&held, key, Entry{..entry, pipeline: Some(built)}), Ok(built))111 case Err(invalid) => (held, Err(Invalid(key, invalid)))112 }113}114