use std::io;
use iroh::endpoint::{self, ClosedStream};
use n0_snafu::SpanTrace;
use nested_enum_utils::common_fields;
use quinn::{ConnectionError, ReadError, WriteError};
use snafu::{Backtrace, IntoError, Snafu};
use crate::{
api::ExportBaoError,
get::fsm::{AtBlobHeaderNextError, ConnectedNextError, DecodeError},
};
#[derive(Debug, Snafu)]
pub enum NotFoundCases {
#[snafu(transparent)]
AtBlobHeaderNext { source: AtBlobHeaderNextError },
#[snafu(transparent)]
Decode { source: DecodeError },
}
#[derive(Debug, Snafu)]
pub enum NoncompliantNodeCases {
#[snafu(transparent)]
Connection { source: ConnectionError },
#[snafu(transparent)]
Decode { source: DecodeError },
}
#[derive(Debug, Snafu)]
pub enum RemoteResetCases {
#[snafu(transparent)]
Read { source: ReadError },
#[snafu(transparent)]
Write { source: WriteError },
#[snafu(transparent)]
Connection { source: ConnectionError },
}
#[derive(Debug, Snafu)]
pub enum BadRequestCases {
#[snafu(transparent)]
Anyhow { source: anyhow::Error },
#[snafu(transparent)]
Postcard { source: postcard::Error },
#[snafu(transparent)]
ConnectedNext { source: ConnectedNextError },
}
#[derive(Debug, Snafu)]
pub enum LocalFailureCases {
#[snafu(transparent)]
Io {
source: io::Error,
},
#[snafu(transparent)]
Anyhow {
source: anyhow::Error,
},
#[snafu(transparent)]
IrpcSend {
source: irpc::channel::SendError,
},
#[snafu(transparent)]
Irpc {
source: irpc::Error,
},
#[snafu(transparent)]
ExportBao {
source: ExportBaoError,
},
TokioSend {},
}
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for LocalFailureCases {
fn from(_: tokio::sync::mpsc::error::SendError<T>) -> Self {
LocalFailureCases::TokioSend {}
}
}
#[derive(Debug, Snafu)]
pub enum IoCases {
#[snafu(transparent)]
Io { source: io::Error },
#[snafu(transparent)]
ConnectionError { source: endpoint::ConnectionError },
#[snafu(transparent)]
ReadError { source: endpoint::ReadError },
#[snafu(transparent)]
WriteError { source: endpoint::WriteError },
#[snafu(transparent)]
ClosedStream { source: endpoint::ClosedStream },
#[snafu(transparent)]
ConnectedNextError { source: ConnectedNextError },
#[snafu(transparent)]
AtBlobHeaderNextError { source: AtBlobHeaderNextError },
}
#[common_fields({
backtrace: Option<Backtrace>,
#[snafu(implicit)]
span_trace: SpanTrace,
})]
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum GetError {
#[snafu(display("Data for hash not found"))]
NotFound {
#[snafu(source(from(NotFoundCases, Box::new)))]
source: Box<NotFoundCases>,
},
#[snafu(display("Remote has reset the connection"))]
RemoteReset {
#[snafu(source(from(RemoteResetCases, Box::new)))]
source: Box<RemoteResetCases>,
},
#[snafu(display("Remote behaved in a non-compliant way"))]
NoncompliantNode {
#[snafu(source(from(NoncompliantNodeCases, Box::new)))]
source: Box<NoncompliantNodeCases>,
},
#[snafu(display("A network or IO operation failed"))]
Io {
#[snafu(source(from(IoCases, Box::new)))]
source: Box<IoCases>,
},
#[snafu(display("Our download request is invalid"))]
BadRequest {
#[snafu(source(from(BadRequestCases, Box::new)))]
source: Box<BadRequestCases>,
},
#[snafu(display("Operation failed on the local node"))]
LocalFailure {
#[snafu(source(from(LocalFailureCases, Box::new)))]
source: Box<LocalFailureCases>,
},
}
pub type GetResult<T> = std::result::Result<T, GetError>;
impl From<irpc::channel::SendError> for GetError {
fn from(value: irpc::channel::SendError) -> Self {
LocalFailureSnafu.into_error(value.into())
}
}
impl<T: Send + Sync + 'static> From<tokio::sync::mpsc::error::SendError<T>> for GetError {
fn from(value: tokio::sync::mpsc::error::SendError<T>) -> Self {
LocalFailureSnafu.into_error(value.into())
}
}
impl From<endpoint::ConnectionError> for GetError {
fn from(value: endpoint::ConnectionError) -> Self {
use endpoint::ConnectionError;
match value {
e @ ConnectionError::VersionMismatch => {
NoncompliantNodeSnafu.into_error(e.into())
}
e @ ConnectionError::TransportError(_) => {
NoncompliantNodeSnafu.into_error(e.into())
}
e @ ConnectionError::ConnectionClosed(_) => {
IoSnafu.into_error(e.into())
}
e @ ConnectionError::ApplicationClosed(_) => {
IoSnafu.into_error(e.into())
}
e @ ConnectionError::Reset => {
RemoteResetSnafu.into_error(e.into())
}
e @ ConnectionError::TimedOut => {
IoSnafu.into_error(e.into())
}
e @ ConnectionError::LocallyClosed => {
IoSnafu.into_error(e.into())
}
e @ ConnectionError::CidsExhausted => {
IoSnafu.into_error(e.into())
}
}
}
}
impl From<endpoint::ReadError> for GetError {
fn from(value: endpoint::ReadError) -> Self {
use endpoint::ReadError;
match value {
e @ ReadError::Reset(_) => RemoteResetSnafu.into_error(e.into()),
ReadError::ConnectionLost(conn_error) => conn_error.into(),
ReadError::ClosedStream
| ReadError::IllegalOrderedRead
| ReadError::ZeroRttRejected => {
IoSnafu.into_error(value.into())
}
}
}
}
impl From<ClosedStream> for GetError {
fn from(value: ClosedStream) -> Self {
IoSnafu.into_error(value.into())
}
}
impl From<quinn::WriteError> for GetError {
fn from(value: quinn::WriteError) -> Self {
use quinn::WriteError;
match value {
e @ WriteError::Stopped(_) => RemoteResetSnafu.into_error(e.into()),
WriteError::ConnectionLost(conn_error) => conn_error.into(),
WriteError::ClosedStream | WriteError::ZeroRttRejected => {
IoSnafu.into_error(value.into())
}
}
}
}
impl From<crate::get::fsm::ConnectedNextError> for GetError {
fn from(value: crate::get::fsm::ConnectedNextError) -> Self {
use crate::get::fsm::ConnectedNextError::*;
match value {
e @ PostcardSer { .. } => {
BadRequestSnafu.into_error(e.into())
}
e @ RequestTooBig { .. } => {
BadRequestSnafu.into_error(e.into())
}
Write { source, .. } => source.into(),
Closed { source, .. } => source.into(),
e @ Io { .. } => {
IoSnafu.into_error(e.into())
}
}
}
}
impl From<crate::get::fsm::AtBlobHeaderNextError> for GetError {
fn from(value: crate::get::fsm::AtBlobHeaderNextError) -> Self {
use crate::get::fsm::AtBlobHeaderNextError::*;
match value {
e @ NotFound { .. } => {
NotFoundSnafu.into_error(e.into())
}
EndpointRead { source, .. } => source.into(),
e @ Io { .. } => {
IoSnafu.into_error(e.into())
}
}
}
}
impl From<crate::get::fsm::DecodeError> for GetError {
fn from(value: crate::get::fsm::DecodeError) -> Self {
use crate::get::fsm::DecodeError::*;
match value {
e @ ChunkNotFound { .. } => NotFoundSnafu.into_error(e.into()),
e @ ParentNotFound { .. } => NotFoundSnafu.into_error(e.into()),
e @ LeafNotFound { .. } => NotFoundSnafu.into_error(e.into()),
e @ ParentHashMismatch { .. } => {
NoncompliantNodeSnafu.into_error(e.into())
}
e @ LeafHashMismatch { .. } => {
NoncompliantNodeSnafu.into_error(e.into())
}
Read { source, .. } => source.into(),
DecodeIo { source, .. } => source.into(),
}
}
}
impl From<std::io::Error> for GetError {
fn from(value: std::io::Error) -> Self {
IoSnafu.into_error(value.into())
}
}