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

PuduLangMediator.pudu

Pudu113 lines3.7 KB

GitHub ↗
1/** @Mediator.Root.Vocabulary — outcomes and failures of mediated messages */2module PuduLangMediator34import PuduLangMediator.Constants.Messages as Messages5import PuduLangMediator.Utils.Template as Template67/** @Mediator.Root.Violation — one rule a message broke */8export type Violation = { property: Str, message: Str, code: Str }910/** @Mediator.Root.Failure — why a mediated message produced no value */11export type Failure[E]12  = Raised(E)13  | Unhandled(Str)14  | Mismatched(Str)15  | Refused(Str)16  | Invalid(Array[Violation])17  | Cancelled(Str)18  | Crashed(Str)19  | Aggregate(Array[Failure[E]])2021/** @Mediator.Root.Outcome — the value or failure of one message */22export type Outcome[T, E] = Result[T, Failure[E]]2324/// An outcome holding a value.25export fn succeed[T, E](value: T) -> Outcome[T, E] { Ok(value) }2627/// An outcome holding the handler's own error.28export fn raise[T, E](problem: E) -> Outcome[T, E] { Err(Raised(problem)) }2930/// An outcome refused for `reason`; the failure an open component answers, since it cannot name `E`.31export fn refuse[T, E](reason: Str) -> Outcome[T, E] { Err(Refused(reason)) }3233/// The outcome of a handler that answers a plain `Result`.34export fn lift[T, E](result: Result[T, E]) -> Outcome[T, E] {35  match result {36    case Ok(value) => Ok(value)37    case Err(problem) => Err(Raised(problem))38  }39}4041/// The handler's own error, when the failure is one.42export fn raised[E](failure: &Failure[E]) -> Option[E] {43  match failure {44    case Raised(problem) => Some(problem)45    case _ => None46  }47}4849/// Whether the failure is a cancellation.50export fn isCancellation[E](failure: &Failure[E]) -> Bool {51  match failure {52    case Cancelled(_) => true53    case _ => false54  }55}5657/// Every violation the failure holds, including those inside an aggregate, in order.58export fn violations[E](failure: &Failure[E]) -> Array[Violation] {59  match failure {60    case Invalid(found) => found61    case Aggregate(parts) => {62      var found: Array[Violation] = []63      for part in parts { found = found.concat(violations(&part)) }64      found65    }66    case _ => []67  }68}6970/// No failure for none, the failure itself for one, and one flat `Aggregate` for several.71export fn combine[E](failures: Array[Failure[E]]) -> Option[Failure[E]] {72  var flat: Array[Failure[E]] = []73  for failure in failures {74    match failure {75      case Aggregate(parts) => { flat = flat.concat(parts) }76      case single => { flat = flat.push(single) }77    }78  }79  match flat {80    case [] => None81    case [only] => Some(only)82    case _ => Some(Aggregate(flat))83  }84}8586/// One sentence saying what went wrong.87export fn describe[E](failure: &Failure[E]) -> Str {88  match failure {89    case Raised(problem) => Template.fill(Messages.RAISED, &[show(problem)])90    case Unhandled(name) => Template.fill(Messages.UNHANDLED, &[name])91    case Mismatched(name) => Template.fill(Messages.MISMATCHED, &[name])92    case Refused(reason) => Template.fill(Messages.REFUSED, &[reason])93    case Invalid(found) => {94      let listed = found.map(|one: Violation| Template.fill(Messages.VIOLATION, &[one.property, one.message]))95      Template.fill(Messages.INVALID, &[listed.join(Messages.SEPARATOR)])96    }97    case Cancelled(reason) => Template.fill(Messages.CANCELLED, &[reason])98    case Crashed(reason) => Template.fill(Messages.CRASHED, &[reason])99    case Aggregate(parts) => {100      let listed = parts.map(|part: Failure[E]| describe(&part))101      Template.fill(Messages.AGGREGATE, &[show(parts.length()), listed.join(Messages.SEPARATOR)])102    }103  }104}105106/// One line naming an outcome.107export fn summarize[T, E](outcome: &Outcome[T, E]) -> Str {108  match outcome {109    case Ok(value) => Template.fill(Messages.SUCCEEDED, &[show(value)])110    case Err(failure) => describe(&failure)111  }112}113