flybrain/services/flysim/crates/flybus/src/error.rs
acamilo 093db147eb feat(flybus): add standalone IPC bus and conformance tests
(cherry picked from commit 95ac8d1bdbc866a7c49fad3ddba468d945267650)
2026-09-22 03:46:27 +00:00

154 lines
4.5 KiB
Rust

//! Transport error codes and the error value every fallible call returns.
use std::fmt;
/// Transport error codes (bus-v1 section 9, plus the three this crate adds; see the README).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ErrorCode {
InvalidEnvelope,
VersionMismatch,
NotAuthorized,
NoService,
TargetChanged,
Backpressure,
CallGone,
ArtifactUnsealed,
ArtifactGone,
OwnerInvalid,
QuotaExceeded,
StoreFailure,
RouterLost,
/// Addition: a name is already registered, a redeclaration disagrees, or a topic still has
/// subscribers.
Conflict,
/// Addition: publish or subscribe named a topic nobody declared.
NoTopic,
/// Addition: a seal found the wrong length or digest, or a reference disagrees with the
/// artifact it names.
ArtifactMismatch,
}
impl ErrorCode {
pub const ALL: [ErrorCode; 16] = [
ErrorCode::InvalidEnvelope,
ErrorCode::VersionMismatch,
ErrorCode::NotAuthorized,
ErrorCode::NoService,
ErrorCode::TargetChanged,
ErrorCode::Backpressure,
ErrorCode::CallGone,
ErrorCode::ArtifactUnsealed,
ErrorCode::ArtifactGone,
ErrorCode::OwnerInvalid,
ErrorCode::QuotaExceeded,
ErrorCode::StoreFailure,
ErrorCode::RouterLost,
ErrorCode::Conflict,
ErrorCode::NoTopic,
ErrorCode::ArtifactMismatch,
];
pub fn as_str(self) -> &'static str {
match self {
ErrorCode::InvalidEnvelope => "INVALID_ENVELOPE",
ErrorCode::VersionMismatch => "VERSION_MISMATCH",
ErrorCode::NotAuthorized => "NOT_AUTHORIZED",
ErrorCode::NoService => "NO_SERVICE",
ErrorCode::TargetChanged => "TARGET_CHANGED",
ErrorCode::Backpressure => "BACKPRESSURE",
ErrorCode::CallGone => "CALL_GONE",
ErrorCode::ArtifactUnsealed => "ARTIFACT_UNSEALED",
ErrorCode::ArtifactGone => "ARTIFACT_GONE",
ErrorCode::OwnerInvalid => "OWNER_INVALID",
ErrorCode::QuotaExceeded => "QUOTA_EXCEEDED",
ErrorCode::StoreFailure => "STORE_FAILURE",
ErrorCode::RouterLost => "ROUTER_LOST",
ErrorCode::Conflict => "CONFLICT",
ErrorCode::NoTopic => "NO_TOPIC",
ErrorCode::ArtifactMismatch => "ARTIFACT_MISMATCH",
}
}
pub fn parse(s: &str) -> Option<ErrorCode> {
ErrorCode::ALL.into_iter().find(|c| c.as_str() == s)
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
/// Whether the operation may have reached its target (bus-v1 section 5).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Dispatch {
NotDispatched,
Dispatched,
Unknown,
}
impl Dispatch {
pub fn as_str(self) -> &'static str {
match self {
Dispatch::NotDispatched => "not-dispatched",
Dispatch::Dispatched => "dispatched",
Dispatch::Unknown => "unknown",
}
}
pub fn parse(s: &str) -> Option<Dispatch> {
match s {
"not-dispatched" => Some(Dispatch::NotDispatched),
"dispatched" => Some(Dispatch::Dispatched),
"unknown" => Some(Dispatch::Unknown),
_ => None,
}
}
}
/// A transport error: code, a short message and the dispatch certainty.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BusError {
pub code: ErrorCode,
pub message: String,
pub dispatch: Dispatch,
}
impl BusError {
/// An error raised before anything could have been dispatched.
pub fn new(code: ErrorCode, message: impl Into<String>) -> BusError {
BusError {
code,
message: message.into(),
dispatch: Dispatch::NotDispatched,
}
}
pub fn with_dispatch(mut self, dispatch: Dispatch) -> BusError {
self.dispatch = dispatch;
self
}
pub(crate) fn invalid(message: impl Into<String>) -> BusError {
BusError::new(ErrorCode::InvalidEnvelope, message)
}
pub(crate) fn lost(message: impl Into<String>) -> BusError {
BusError::new(ErrorCode::RouterLost, message).with_dispatch(Dispatch::Unknown)
}
}
impl fmt::Display for BusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} ({}): {}",
self.code,
self.dispatch.as_str(),
self.message
)
}
}
impl std::error::Error for BusError {}