pub use zyris_proto::{ErrorCode, WireError};
pub type Error = WireError;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Clone, thiserror::Error)]
pub enum TransportError {
#[error("transport closed")]
Closed,
#[error("transport error: {0}")]
Io(String),
}
impl From<TransportError> for WireError {
fn from(err: TransportError) -> Self {
match err {
TransportError::Closed => WireError::connection_lost(),
TransportError::Io(msg) => {
WireError::new(ErrorCode::ConnectionLost, msg).retriable(true)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CloseReason {
Graceful(String),
Transport(String),
Protocol(String),
Heartbeat(String),
}
impl std::fmt::Display for CloseReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CloseReason::Graceful(r) => write!(f, "graceful close: {r}"),
CloseReason::Transport(r) => write!(f, "transport closed: {r}"),
CloseReason::Protocol(r) => write!(f, "protocol error: {r}"),
CloseReason::Heartbeat(r) => write!(f, "heartbeat lapsed: {r}"),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ConnectError {
#[error("this credential was revoked; a person must authorize this node again")]
Revoked,
#[error("the server did not accept this token")]
Unauthorized,
#[error(
"protocol mismatch: this node speaks {ours}, the server speaks {}",
theirs.as_deref().unwrap_or("unknown")
)]
VersionMismatch { ours: String, theirs: Option<String> },
#[error("could not reach the server: {0}")]
Unreachable(#[from] TransportError),
#[error(
"this build selected no TLS provider, so it cannot dial wss://: turn on the `tls-ring` \
or `tls-aws-lc` feature of this crate, or install one yourself before dialling with \
`rustls::crypto::CryptoProvider::install_default`"
)]
NoTlsProvider,
}
impl From<WireError> for ConnectError {
fn from(err: WireError) -> ConnectError {
match err.code {
ErrorCode::Unauthorized => ConnectError::Unauthorized,
ErrorCode::UnsupportedVersion => ConnectError::VersionMismatch {
ours: zyris_proto::PROTOCOL_MAJOR.to_string(),
theirs: peer_major(&err),
},
_ => ConnectError::Unreachable(TransportError::Io(err.message)),
}
}
}
impl From<crate::enroll::protocol::RefreshOutcome> for ConnectError {
fn from(outcome: crate::enroll::protocol::RefreshOutcome) -> ConnectError {
use crate::enroll::protocol::RefreshOutcome;
match outcome {
RefreshOutcome::Dead(_) => ConnectError::Revoked,
RefreshOutcome::Unavailable(reason) => {
ConnectError::Unreachable(TransportError::Io(reason))
}
}
}
}
fn peer_major(error: &WireError) -> Option<String> {
error
.data
.as_ref()
.and_then(|data| data.to_json().ok())
.and_then(|json| json.get("peer_major").and_then(serde_json::Value::as_u64))
.map(|major| major.to_string())
}
#[derive(Debug, thiserror::Error)]
pub enum EnrollError {
#[error("the request was declined")]
Denied,
#[error("the code expired")]
Lapsed,
#[error("this credential was revoked; a person must authorize this node again")]
Revoked,
#[error("the server does not know the scope {scope}")]
ScopeUnknown { scope: String },
#[error("could not reach the server: {0}")]
Unreachable(#[from] TransportError),
}
#[derive(Debug, thiserror::Error)]
pub enum RegisterError {
#[error("this credential may not register nodes; it lacks nodes:write")]
Forbidden,
#[error("requested scopes exceed this credential's grant")]
ScopeExceeded { requested: Vec<String>, granted: Vec<String> },
#[error("this credential was revoked; a person must authorize this account again")]
Revoked,
#[error("could not reach the server: {0}")]
Unreachable(#[from] TransportError),
}
#[derive(Debug, thiserror::Error)]
#[error("the credential could not be stored: {0}")]
pub struct RotateError(pub String);