use thiserror::Error;
#[derive(Debug, Error)]
pub enum BusError {
#[error("not running")]
NotRunning,
#[error("no peer for topic {0}")]
NoSubscriber(String),
#[error("peer not reachable: {0}")]
Unreachable(String),
#[error("request timed out after {0:?}")]
Timeout(std::time::Duration),
#[error("reply correlation lost")]
LostReply,
#[error("replay rejected: nonce already seen")]
Replay,
#[error("sequence error: peer={peer_fp}, expected {expected}, got {actual}")]
BadSequence {
peer_fp: String,
expected: u64,
actual: u64,
},
#[error("transport: {0}")]
Transport(#[from] agent_mesh_transport::TransportError),
#[error("core: {0}")]
Core(#[from] agent_mesh_protocol::MeshError),
#[error("json: {0}")]
Json(#[from] serde_json::Error),
}
pub type Result<T> = std::result::Result<T, BusError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn replay_renders_useful_message() {
let e = BusError::Replay;
assert!(format!("{e}").contains("replay"));
}
#[test]
fn bad_sequence_includes_peer_and_numbers() {
let e = BusError::BadSequence {
peer_fp: "abc123".into(),
expected: 5,
actual: 3,
};
let msg = format!("{e}");
assert!(msg.contains("abc123"));
assert!(msg.contains('5'));
assert!(msg.contains('3'));
}
#[test]
fn timeout_renders_duration() {
let e = BusError::Timeout(std::time::Duration::from_secs(2));
assert!(format!("{e}").contains('2'));
}
#[test]
fn transport_error_converts_via_from() {
let t = agent_mesh_transport::TransportError::Iroh("bind failed".into());
let e: BusError = t.into();
assert!(matches!(e, BusError::Transport(_)));
}
#[test]
fn core_error_converts_via_from() {
let c = agent_mesh_protocol::MeshError::BadSignature;
let e: BusError = c.into();
assert!(matches!(e, BusError::Core(_)));
}
#[test]
fn json_error_converts_via_from() {
let r: std::result::Result<serde_json::Value, _> = serde_json::from_str("not-json");
let je = r.unwrap_err();
let e: BusError = je.into();
assert!(matches!(e, BusError::Json(_)));
}
}