///|
/// One handler failure in a publish. Only mediator constructs this record.
pub struct PublishFailure {
/// The 1-based position of the handler in the topic, in add order.
position : Int
/// The error that the handler raised, unchanged.
error : Error
}
///|
/// An immutable failure collection. Accessors do not expose its storage.
struct PublishFailures {
items : Array[PublishFailure]
}
///|
/// Give the number of failed handlers.
pub fn PublishFailures::length(self : PublishFailures) -> Int {
self.items.length()
}
///|
/// Give the failure at a zero-based index, or None when the index is outside
/// the collection.
pub fn PublishFailures::get(self : PublishFailures, index : Int) -> PublishFailure? {
self.items.get(index)
}
///|
/// Give an array copy. Changes to this array do not change the collection.
pub fn PublishFailures::to_array(self : PublishFailures) -> Array[PublishFailure] {
self.items.copy()
}
///|
/// A mediator-level failure. Callers can match cases but use the supported
/// constructors to make errors. Application errors from send pass unchanged.
pub suberror MediatorError {
/// A handler or middleware refuses a request. The reason must not hold
/// request values, because a request can hold secrets.
Rejected(reason~ : String)
/// One or more handlers failed. The count and positions come from the
/// handler outcomes; the failure collection cannot change.
PublishFailed(handlers~ : Int, failures~ : PublishFailures)
/// A step did not end within the shared core time limit.
TimedOut(after~ : @lander_core.TimeLimit)
}
///|
/// Refuse a request or event. Use a reason that holds no request values.
pub fn MediatorError::rejected(reason : String) -> MediatorError {
Rejected(reason~)
}
///|
/// Describe a timeout with a valid shared core time limit.
pub fn MediatorError::timed_out(after : @lander_core.TimeLimit) -> MediatorError {
TimedOut(after~)
}
///|
/// Derive a publish error from one optional error per handler in add order.
/// None is a successful handler. Empty and all-success arrays give None.
/// Failed slots derive the count and positive positions. The error retains
/// no caller-owned array, so later changes to `errors` do not change it.
pub fn MediatorError::from_publish_errors(
errors : Array[Error?],
) -> MediatorError? {
let failures = []
for index, error in errors {
if error is Some(error) {
failures.push({ position: index + 1, error })
}
}
if failures.is_empty() {
None
} else {
Some(PublishFailed(handlers=errors.length(), failures={ items: failures }))
}
}
///|
/// Text for people. It holds only the reason, counts and positions, or time
/// limit. It never formats request values, events or wrapped error text.
pub fn MediatorError::message(self : MediatorError) -> String {
match self {
Rejected(reason~) => "Request rejected: \{reason}"
PublishFailed(handlers~, failures~) => {
let positions = failures.items.map(failure => failure.position.to_string())
let label = if positions.length() == 1 { "position" } else { "positions" }
"Publish failed: \{failures.length()} of \{handlers} handlers raised an error (\{label} \{positions.join(", ")})"
}
TimedOut(after~) => "Request timed out after \{after.to_millis()} ms"
}
}