bao_tree/io/
error.rs

1//! Errors when encoding or decoding
2//!
3//! These erros contain more specific information about e.g. where a hash mismatch occured
4use crate::{ChunkNum, TreeNode};
5use std::{fmt, io};
6
7/// Error when decoding from a reader, after the size has been read
8#[derive(Debug)]
9pub enum DecodeError {
10    /// We got an EOF while reading a parent hash pair, indicating that the remote end does not have the outboard
11    ParentNotFound(TreeNode),
12    /// We got an EOF while reading a chunk, indicating that the remote end does not have the data
13    LeafNotFound(ChunkNum),
14    /// The hash of a parent did not match the expected hash
15    ParentHashMismatch(TreeNode),
16    /// The hash of a leaf did not match the expected hash
17    LeafHashMismatch(ChunkNum),
18    /// There was an error reading from the underlying io
19    Io(io::Error),
20}
21
22impl fmt::Display for DecodeError {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        fmt::Debug::fmt(self, f)
25    }
26}
27
28impl std::error::Error for DecodeError {
29    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
30        match self {
31            Self::Io(e) => Some(e),
32            _ => None,
33        }
34    }
35}
36
37impl From<io::Error> for DecodeError {
38    fn from(e: io::Error) -> Self {
39        Self::Io(e)
40    }
41}
42
43impl From<DecodeError> for io::Error {
44    fn from(e: DecodeError) -> Self {
45        match e {
46            DecodeError::Io(e) => e,
47            DecodeError::ParentHashMismatch(node) => io::Error::new(
48                io::ErrorKind::InvalidData,
49                format!(
50                    "parent hash mismatch (level {}, block {})",
51                    node.level(),
52                    node.mid().0
53                ),
54            ),
55            DecodeError::LeafHashMismatch(chunk) => io::Error::new(
56                io::ErrorKind::InvalidData,
57                format!("leaf hash mismatch (offset {})", chunk.to_bytes()),
58            ),
59            DecodeError::LeafNotFound(_) => io::Error::new(io::ErrorKind::UnexpectedEof, e),
60            DecodeError::ParentNotFound(_) => io::Error::new(io::ErrorKind::UnexpectedEof, e),
61        }
62    }
63}
64
65impl DecodeError {
66    pub(crate) fn maybe_parent_not_found(e: io::Error, node: TreeNode) -> Self {
67        if e.kind() == io::ErrorKind::UnexpectedEof {
68            Self::ParentNotFound(node)
69        } else {
70            Self::Io(e)
71        }
72    }
73
74    pub(crate) fn maybe_leaf_not_found(e: io::Error, chunk: ChunkNum) -> Self {
75        if e.kind() == io::ErrorKind::UnexpectedEof {
76            Self::LeafNotFound(chunk)
77        } else {
78            Self::Io(e)
79        }
80    }
81}
82
83/// Error when encoding from outboard and data
84///
85/// This can either be a io error or a more specific error like a hash mismatch
86/// or a size mismatch. If the remote end stops listening while we are writing,
87/// the error will indicate which parent or chunk we were writing at the time.
88#[derive(Debug)]
89pub enum EncodeError {
90    /// The hash of a parent did not match the expected hash
91    ParentHashMismatch(TreeNode),
92    /// The hash of a leaf did not match the expected hash
93    LeafHashMismatch(ChunkNum),
94    /// We got a ConnectionReset while writing a parent hash pair, indicating that the remote end stopped listening
95    ParentWrite(TreeNode),
96    /// We got a ConnectionReset while writing a chunk, indicating that the remote end stopped listening
97    LeafWrite(ChunkNum),
98    /// File size does not match size in outboard
99    SizeMismatch,
100    /// There was an error reading from the underlying io
101    Io(io::Error),
102}
103
104impl fmt::Display for EncodeError {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        fmt::Debug::fmt(self, f)
107    }
108}
109
110impl std::error::Error for EncodeError {
111    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
112        match self {
113            EncodeError::Io(e) => Some(e),
114            _ => None,
115        }
116    }
117}
118
119impl From<EncodeError> for io::Error {
120    fn from(e: EncodeError) -> Self {
121        match e {
122            EncodeError::Io(e) => e,
123            EncodeError::ParentHashMismatch(node) => io::Error::new(
124                io::ErrorKind::InvalidData,
125                format!(
126                    "parent hash mismatch (level {}, block {})",
127                    node.level(),
128                    node.mid().0
129                ),
130            ),
131            EncodeError::LeafHashMismatch(chunk) => io::Error::new(
132                io::ErrorKind::InvalidData,
133                format!("leaf hash mismatch at {}", chunk.to_bytes()),
134            ),
135            EncodeError::ParentWrite(node) => io::Error::new(
136                io::ErrorKind::ConnectionReset,
137                format!(
138                    "parent write failed (level {}, block {})",
139                    node.level(),
140                    node.mid().0
141                ),
142            ),
143            EncodeError::LeafWrite(chunk) => io::Error::new(
144                io::ErrorKind::ConnectionReset,
145                format!("leaf write failed at {}", chunk.to_bytes()),
146            ),
147            EncodeError::SizeMismatch => {
148                io::Error::new(io::ErrorKind::InvalidData, "size mismatch")
149            }
150        }
151    }
152}
153
154impl From<io::Error> for EncodeError {
155    fn from(e: io::Error) -> Self {
156        Self::Io(e)
157    }
158}
159
160impl EncodeError {
161    #[cfg(feature = "tokio_fsm")]
162    pub(crate) fn maybe_parent_write(e: io::Error, node: TreeNode) -> Self {
163        if e.kind() == io::ErrorKind::ConnectionReset {
164            Self::ParentWrite(node)
165        } else {
166            Self::Io(e)
167        }
168    }
169
170    #[cfg(feature = "tokio_fsm")]
171    pub(crate) fn maybe_leaf_write(e: io::Error, chunk: ChunkNum) -> Self {
172        if e.kind() == io::ErrorKind::ConnectionReset {
173            Self::LeafWrite(chunk)
174        } else {
175            Self::Io(e)
176        }
177    }
178}