#[cfg(relays)]
use crate::relay_client::RelayError;
#[derive(thiserror::Error, Debug)]
pub enum BuildError {
#[error("client configured without DHT client or relays")]
NoNetwork,
#[cfg(dht)]
#[error("failed to build the DHT client: {0}")]
DhtBuildError(std::io::Error),
#[cfg(relays)]
#[error("passed an empty list of relays")]
EmptyListOfRelays,
#[cfg(relays)]
#[error("failed to build the relays client: {0}")]
RelayBuildError(RelayError),
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, Hash)]
pub enum PublishError {
#[error("DHT publish queried no nodes")]
NoDhtNodesQueried,
#[error("publish query received no responses")]
NoResponses,
#[error("DHT publish query was rejected with code: {code}, description: {description}")]
Rejected {
code: i32,
description: String,
},
#[error("found a more recent SignedPacket")]
NotMostRecent,
#[error("all responses were unexpected, check debug logs")]
UnexpectedResponses,
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, Hash)]
pub enum ResolveError {
#[error("DHT resolve queried no nodes")]
NoDhtNodesQueried,
#[error("resolve query received no responses")]
NoResponses,
#[error("resolve query received no usable responses")]
NoUsableResponses,
#[error("SignedPacket was not found")]
NotFound,
#[error("mutable item at seq {seq} is not a valid signed packet")]
InvalidSignedPacket {
seq: i64,
},
#[error("all responses were unexpected, check debug logs")]
UnexpectedResponses,
}
#[cfg(dht)]
impl From<crate::dht::PublishError> for PublishError {
fn from(value: crate::dht::PublishError) -> Self {
match value {
crate::dht::PublishError::Timeout => PublishError::NoResponses,
crate::dht::PublishError::NoClosestNodes => PublishError::NoDhtNodesQueried,
crate::dht::PublishError::ErrorResponse { code, description } => {
PublishError::Rejected { code, description }
}
crate::dht::PublishError::NotMostRecent => PublishError::NotMostRecent,
}
}
}
#[cfg(relays)]
impl From<RelayError> for PublishError {
fn from(value: RelayError) -> Self {
match value {
RelayError::Timeout | RelayError::Request(_) | RelayError::DhtUnavailable => {
PublishError::NoResponses
}
RelayError::NotMostRecent => PublishError::NotMostRecent,
_ => PublishError::UnexpectedResponses,
}
}
}
#[cfg(dht)]
impl From<crate::dht::ResolveError> for ResolveError {
fn from(value: crate::dht::ResolveError) -> Self {
match value {
crate::dht::ResolveError::NoNodesQueried => ResolveError::NoDhtNodesQueried,
crate::dht::ResolveError::NoNodesResponded => ResolveError::NoResponses,
crate::dht::ResolveError::NoUsableResponses => ResolveError::NoUsableResponses,
crate::dht::ResolveError::InvalidSignedPacket { seq } => {
ResolveError::InvalidSignedPacket { seq }
}
crate::dht::ResolveError::NotFound => ResolveError::NotFound,
}
}
}
#[cfg(relays)]
impl From<RelayError> for ResolveError {
fn from(value: RelayError) -> Self {
match value {
RelayError::Timeout | RelayError::Request(_) | RelayError::DhtUnavailable => {
ResolveError::NoResponses
}
RelayError::NotFound => ResolveError::NotFound,
RelayError::InvalidSignedPacketSeq { seq } => ResolveError::InvalidSignedPacket { seq },
_ => ResolveError::UnexpectedResponses,
}
}
}
#[cfg(all(test, relays))]
mod relay_tests {
use super::*;
use crate::relay_client::RelayError;
fn relay_request_error() -> RelayError {
let error = reqwest::Client::new().get("://").build().unwrap_err();
RelayError::Request(error)
}
#[test]
fn dht_relay_errors_map_to_publish_errors() {
assert!(matches!(
PublishError::from(RelayError::DhtUnavailable),
PublishError::NoResponses
));
}
#[test]
fn dht_relay_errors_map_to_resolve_errors() {
assert!(matches!(
ResolveError::from(RelayError::DhtUnavailable),
ResolveError::NoResponses
));
}
#[test]
fn relay_timeouts_map_to_operation_specific_errors() {
assert_eq!(
PublishError::from(RelayError::Timeout),
PublishError::NoResponses
);
assert_eq!(
ResolveError::from(RelayError::Timeout),
ResolveError::NoResponses
);
}
#[test]
fn relay_request_failures_map_to_no_response_errors() {
assert_eq!(
PublishError::from(relay_request_error()),
PublishError::NoResponses
);
assert_eq!(
ResolveError::from(relay_request_error()),
ResolveError::NoResponses
);
}
#[test]
fn relay_bad_request_is_an_unexpected_resolve_response() {
assert_eq!(
ResolveError::from(RelayError::BadRequest),
ResolveError::UnexpectedResponses
);
}
#[test]
fn relay_bad_request_is_an_unexpected_publish_response() {
assert_eq!(
PublishError::from(RelayError::BadRequest),
PublishError::UnexpectedResponses
);
}
}
#[cfg(all(test, dht))]
mod dht_tests {
use super::*;
#[test]
fn dht_publish_without_closest_nodes_maps_to_no_dht_nodes_queried() {
assert_eq!(
PublishError::from(crate::dht::PublishError::NoClosestNodes),
PublishError::NoDhtNodesQueried
);
}
#[test]
fn dht_resolve_without_responses_is_not_a_generic_query_timeout() {
assert_eq!(
ResolveError::from(crate::dht::ResolveError::NoNodesResponded),
ResolveError::NoResponses
);
}
#[test]
fn dht_resolve_without_usable_responses_is_preserved() {
assert_eq!(
ResolveError::from(crate::dht::ResolveError::NoUsableResponses),
ResolveError::NoUsableResponses
);
}
}