Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-resilience

Resilience pipelines for Pudu: retry, circuit breaker, timeout, fallback, hedging, rate limiting, and chaos injection

0.1.0Apache-2.01

InstallClose

Predicate.pudu

Pudu111 lines3.4 KB

GitHub ↗
1/** @Resilience.Predicate.Module — which outcomes a reactive strategy handles */2module PuduLangResilience.Predicate34import PuduLangResilience.Context as Context5import PuduLangResilience as Resilience67/** @Resilience.Predicate.Arguments — the outcome being judged and where it arose */8export type Arguments[T, E] = { outcome: Resilience.Outcome[T, E], context: Context.Context, attempt: Int }910/** @Resilience.Predicate.Predicate — answers whether a strategy handles an outcome */11export type Predicate[T, E] = fn(Arguments[T, E]) -> Bool1213/// The arguments for judging one outcome. `attempt` counts from 0 for the first execution.14export fn arguments[T, E](outcome: Resilience.Outcome[T, E], context: Context.Context, attempt: Int) -> Arguments[T, E] {15  Arguments{outcome: outcome, context: context, attempt: attempt}16}1718/// Whether `test` handles the outcome.19export fn handles[T, E](test: &Predicate[T, E], given: &Arguments[T, E]) -> Bool { (*test)(*given) }2021/// Every failure except a cancellation; no value. The default of every reactive strategy.22export fn failures[T, E]() -> Predicate[T, E] {23  fn(given: Arguments[T, E]) -> Bool {24    match given.outcome {25      case Ok(_) => false26      case Err(found) => !Resilience.isCancellation(&found)27    }28  }29}3031/// The callback's own errors that `test` accepts.32export fn raised[T, E](test: fn(E) -> Bool) -> Predicate[T, E] {33  fn(given: Arguments[T, E]) -> Bool {34    match given.outcome {35      case Err(Resilience.Raised(problem)) => test(problem)36      case _ => false37    }38  }39}4041/// Every error the callback raises itself.42export fn anyRaised[T, E]() -> Predicate[T, E] { raised(fn(_problem: E) -> Bool { true }) }4344/// The failures, of any kind, that `test` accepts.45export fn failure[T, E](test: fn(Resilience.Failure[E]) -> Bool) -> Predicate[T, E] {46  fn(given: Arguments[T, E]) -> Bool {47    match given.outcome {48      case Err(found) => test(found)49      case Ok(_) => false50    }51  }52}5354/// The values that `test` accepts.55export fn results[T, E](test: fn(T) -> Bool) -> Predicate[T, E] {56  fn(given: Arguments[T, E]) -> Bool {57    match given.outcome {58      case Ok(value) => test(value)59      case Err(_) => false60    }61  }62}6364/// The values equal to `expected`.65export fn result[T, E](expected: T) -> Predicate[T, E] { results(fn(value: T) -> Bool { value == expected }) }6667/// Rejections by a timeout strategy.68export fn timeouts[T, E]() -> Predicate[T, E] {69  failure(fn(found: Resilience.Failure[E]) -> Bool {70      match found {71        case Resilience.TimedOut(_) => true72        case _ => false73      }74    })75}7677/// Rejections by an open or isolated circuit.78export fn brokenCircuits[T, E]() -> Predicate[T, E] {79  failure(fn(found: Resilience.Failure[E]) -> Bool {80      match found {81        case Resilience.BrokenCircuit(_) => true82        case Resilience.IsolatedCircuit => true83        case _ => false84      }85    })86}8788/// Rejections by a rate limiter.89export fn rateLimited[T, E]() -> Predicate[T, E] {90  failure(fn(found: Resilience.Failure[E]) -> Bool {91      match found {92        case Resilience.RateLimited(_) => true93        case _ => false94      }95    })96}9798/// Outcomes that any of `tests` handles; none when `tests` is empty.99export fn anyOf[T, E](tests: &Array[Predicate[T, E]]) -> Predicate[T, E] {100  let all = *tests101  fn(given: Arguments[T, E]) -> Bool {102    for test in all {103      if test(given) { return true }104    }105    false106  }107}108109/// Handles nothing.110export fn never[T, E]() -> Predicate[T, E] { fn(_given: Arguments[T, E]) -> Bool { false } }111