1use std::io;
3
4use iroh::endpoint::{self, ClosedStream};
5use n0_snafu::SpanTrace;
6use nested_enum_utils::common_fields;
7use quinn::{ConnectionError, ReadError, WriteError};
8use snafu::{Backtrace, IntoError, Snafu};
9
10use crate::{
11 api::ExportBaoError,
12 get::fsm::{AtBlobHeaderNextError, ConnectedNextError, DecodeError},
13};
14
15#[derive(Debug, Snafu)]
16pub enum NotFoundCases {
17 #[snafu(transparent)]
18 AtBlobHeaderNext { source: AtBlobHeaderNextError },
19 #[snafu(transparent)]
20 Decode { source: DecodeError },
21}
22
23#[derive(Debug, Snafu)]
24pub enum NoncompliantNodeCases {
25 #[snafu(transparent)]
26 Connection { source: ConnectionError },
27 #[snafu(transparent)]
28 Decode { source: DecodeError },
29}
30
31#[derive(Debug, Snafu)]
32pub enum RemoteResetCases {
33 #[snafu(transparent)]
34 Read { source: ReadError },
35 #[snafu(transparent)]
36 Write { source: WriteError },
37 #[snafu(transparent)]
38 Connection { source: ConnectionError },
39}
40
41#[derive(Debug, Snafu)]
42pub enum BadRequestCases {
43 #[snafu(transparent)]
44 Anyhow { source: anyhow::Error },
45 #[snafu(transparent)]
46 Postcard { source: postcard::Error },
47 #[snafu(transparent)]
48 ConnectedNext { source: ConnectedNextError },
49}
50
51#[derive(Debug, Snafu)]
52pub enum LocalFailureCases {
53 #[snafu(transparent)]
54 Io {
55 source: io::Error,
56 },
57 #[snafu(transparent)]
58 Anyhow {
59 source: anyhow::Error,
60 },
61 #[snafu(transparent)]
62 IrpcSend {
63 source: irpc::channel::SendError,
64 },
65 #[snafu(transparent)]
66 Irpc {
67 source: irpc::Error,
68 },
69 #[snafu(transparent)]
70 ExportBao {
71 source: ExportBaoError,
72 },
73 TokioSend {},
74}
75
76impl<T> From<tokio::sync::mpsc::error::SendError<T>> for LocalFailureCases {
77 fn from(_: tokio::sync::mpsc::error::SendError<T>) -> Self {
78 LocalFailureCases::TokioSend {}
79 }
80}
81
82#[derive(Debug, Snafu)]
83pub enum IoCases {
84 #[snafu(transparent)]
85 Io { source: io::Error },
86 #[snafu(transparent)]
87 ConnectionError { source: endpoint::ConnectionError },
88 #[snafu(transparent)]
89 ReadError { source: endpoint::ReadError },
90 #[snafu(transparent)]
91 WriteError { source: endpoint::WriteError },
92 #[snafu(transparent)]
93 ClosedStream { source: endpoint::ClosedStream },
94 #[snafu(transparent)]
95 ConnectedNextError { source: ConnectedNextError },
96 #[snafu(transparent)]
97 AtBlobHeaderNextError { source: AtBlobHeaderNextError },
98}
99
100#[common_fields({
102 backtrace: Option<Backtrace>,
103 #[snafu(implicit)]
104 span_trace: SpanTrace,
105})]
106#[derive(Debug, Snafu)]
107#[snafu(visibility(pub(crate)))]
108pub enum GetError {
109 #[snafu(display("Data for hash not found"))]
111 NotFound {
112 #[snafu(source(from(NotFoundCases, Box::new)))]
113 source: Box<NotFoundCases>,
114 },
115 #[snafu(display("Remote has reset the connection"))]
117 RemoteReset {
118 #[snafu(source(from(RemoteResetCases, Box::new)))]
119 source: Box<RemoteResetCases>,
120 },
121 #[snafu(display("Remote behaved in a non-compliant way"))]
123 NoncompliantNode {
124 #[snafu(source(from(NoncompliantNodeCases, Box::new)))]
125 source: Box<NoncompliantNodeCases>,
126 },
127
128 #[snafu(display("A network or IO operation failed"))]
130 Io {
131 #[snafu(source(from(IoCases, Box::new)))]
132 source: Box<IoCases>,
133 },
134 #[snafu(display("Our download request is invalid"))]
136 BadRequest {
137 #[snafu(source(from(BadRequestCases, Box::new)))]
138 source: Box<BadRequestCases>,
139 },
140 #[snafu(display("Operation failed on the local node"))]
142 LocalFailure {
143 #[snafu(source(from(LocalFailureCases, Box::new)))]
144 source: Box<LocalFailureCases>,
145 },
146}
147
148pub type GetResult<T> = std::result::Result<T, GetError>;
149
150impl From<irpc::channel::SendError> for GetError {
151 fn from(value: irpc::channel::SendError) -> Self {
152 LocalFailureSnafu.into_error(value.into())
153 }
154}
155
156impl<T: Send + Sync + 'static> From<tokio::sync::mpsc::error::SendError<T>> for GetError {
157 fn from(value: tokio::sync::mpsc::error::SendError<T>) -> Self {
158 LocalFailureSnafu.into_error(value.into())
159 }
160}
161
162impl From<endpoint::ConnectionError> for GetError {
163 fn from(value: endpoint::ConnectionError) -> Self {
164 use endpoint::ConnectionError;
166 match value {
167 e @ ConnectionError::VersionMismatch => {
168 NoncompliantNodeSnafu.into_error(e.into())
171 }
172 e @ ConnectionError::TransportError(_) => {
173 NoncompliantNodeSnafu.into_error(e.into())
176 }
177 e @ ConnectionError::ConnectionClosed(_) => {
178 IoSnafu.into_error(e.into())
181 }
182 e @ ConnectionError::ApplicationClosed(_) => {
183 IoSnafu.into_error(e.into())
186 }
187 e @ ConnectionError::Reset => {
188 RemoteResetSnafu.into_error(e.into())
190 }
191 e @ ConnectionError::TimedOut => {
192 IoSnafu.into_error(e.into())
194 }
195 e @ ConnectionError::LocallyClosed => {
196 IoSnafu.into_error(e.into())
199 }
200 e @ ConnectionError::CidsExhausted => {
201 IoSnafu.into_error(e.into())
204 }
205 }
206 }
207}
208
209impl From<endpoint::ReadError> for GetError {
210 fn from(value: endpoint::ReadError) -> Self {
211 use endpoint::ReadError;
212 match value {
213 e @ ReadError::Reset(_) => RemoteResetSnafu.into_error(e.into()),
214 ReadError::ConnectionLost(conn_error) => conn_error.into(),
215 ReadError::ClosedStream
216 | ReadError::IllegalOrderedRead
217 | ReadError::ZeroRttRejected => {
218 IoSnafu.into_error(value.into())
220 }
221 }
222 }
223}
224impl From<ClosedStream> for GetError {
225 fn from(value: ClosedStream) -> Self {
226 IoSnafu.into_error(value.into())
227 }
228}
229
230impl From<quinn::WriteError> for GetError {
231 fn from(value: quinn::WriteError) -> Self {
232 use quinn::WriteError;
233 match value {
234 e @ WriteError::Stopped(_) => RemoteResetSnafu.into_error(e.into()),
235 WriteError::ConnectionLost(conn_error) => conn_error.into(),
236 WriteError::ClosedStream | WriteError::ZeroRttRejected => {
237 IoSnafu.into_error(value.into())
239 }
240 }
241 }
242}
243
244impl From<crate::get::fsm::ConnectedNextError> for GetError {
245 fn from(value: crate::get::fsm::ConnectedNextError) -> Self {
246 use crate::get::fsm::ConnectedNextError::*;
247 match value {
248 e @ PostcardSer { .. } => {
249 BadRequestSnafu.into_error(e.into())
251 }
252 e @ RequestTooBig { .. } => {
253 BadRequestSnafu.into_error(e.into())
255 }
256 Write { source, .. } => source.into(),
257 Closed { source, .. } => source.into(),
258 e @ Io { .. } => {
259 IoSnafu.into_error(e.into())
261 }
262 }
263 }
264}
265
266impl From<crate::get::fsm::AtBlobHeaderNextError> for GetError {
267 fn from(value: crate::get::fsm::AtBlobHeaderNextError) -> Self {
268 use crate::get::fsm::AtBlobHeaderNextError::*;
269 match value {
270 e @ NotFound { .. } => {
271 NotFoundSnafu.into_error(e.into())
274 }
275 EndpointRead { source, .. } => source.into(),
276 e @ Io { .. } => {
277 IoSnafu.into_error(e.into())
279 }
280 }
281 }
282}
283
284impl From<crate::get::fsm::DecodeError> for GetError {
285 fn from(value: crate::get::fsm::DecodeError) -> Self {
286 use crate::get::fsm::DecodeError::*;
287
288 match value {
289 e @ ChunkNotFound { .. } => NotFoundSnafu.into_error(e.into()),
290 e @ ParentNotFound { .. } => NotFoundSnafu.into_error(e.into()),
291 e @ LeafNotFound { .. } => NotFoundSnafu.into_error(e.into()),
292 e @ ParentHashMismatch { .. } => {
293 NoncompliantNodeSnafu.into_error(e.into())
296 }
297 e @ LeafHashMismatch { .. } => {
298 NoncompliantNodeSnafu.into_error(e.into())
301 }
302 Read { source, .. } => source.into(),
303 DecodeIo { source, .. } => source.into(),
304 }
305 }
306}
307
308impl From<std::io::Error> for GetError {
309 fn from(value: std::io::Error) -> Self {
310 IoSnafu.into_error(value.into())
313 }
314}