bitcoincore_zmq/
error.rs

1use crate::{
2    message::{DATA_MAX_LEN, SEQUENCE_LEN, TOPIC_MAX_LEN},
3    monitor::MonitorMessageError,
4};
5use bitcoin::consensus;
6use core::{cmp::min, fmt};
7
8pub type Result<T> = core::result::Result<T, Error>;
9
10#[derive(Debug)]
11pub enum Error {
12    InvalidMutlipartLength(usize),
13    InvalidTopic(usize, [u8; TOPIC_MAX_LEN]),
14    InvalidDataLength(usize),
15    InvalidSequenceLength(usize),
16    InvalidSequenceMessageLength(usize),
17    InvalidSequenceMessageLabel(u8),
18    Invalid256BitHashLength(usize),
19    BitcoinDeserialization(consensus::encode::Error),
20    Zmq(zmq::Error),
21    MonitorMessage(MonitorMessageError),
22}
23
24impl Error {
25    /// Returns the (invalid) topic as a byte slice (as this might not always be valid UTF-8). If
26    /// this error is not an [`Error::InvalidTopic`], [`None`] is returned. The real length is also
27    /// returned, if this is higher that the length of the slice, the data was truncated to fit
28    /// directly in the object, instead of with a heap allocation.
29    pub fn invalid_topic_data(&self) -> Option<(&[u8], usize)> {
30        if let Self::InvalidTopic(len, buf) = self {
31            Some((&buf[..min(*len, buf.len())], *len))
32        } else {
33            None
34        }
35    }
36}
37
38impl From<zmq::Error> for Error {
39    #[inline]
40    fn from(value: zmq::Error) -> Self {
41        Self::Zmq(value)
42    }
43}
44
45#[cfg(feature = "async")]
46impl From<async_zmq::SocketError> for Error {
47    #[inline]
48    fn from(value: async_zmq::SocketError) -> Self {
49        Self::Zmq(value.into())
50    }
51}
52
53#[cfg(feature = "async")]
54impl From<async_zmq::SubscribeError> for Error {
55    #[inline]
56    fn from(value: async_zmq::SubscribeError) -> Self {
57        Self::Zmq(value.into())
58    }
59}
60
61#[cfg(feature = "async")]
62impl From<async_zmq::RecvError> for Error {
63    #[inline]
64    fn from(value: async_zmq::RecvError) -> Self {
65        Self::Zmq(value.into())
66    }
67}
68
69impl From<consensus::encode::Error> for Error {
70    #[inline]
71    fn from(value: consensus::encode::Error) -> Self {
72        Self::BitcoinDeserialization(value)
73    }
74}
75
76impl From<MonitorMessageError> for Error {
77    #[inline]
78    fn from(value: MonitorMessageError) -> Self {
79        Self::MonitorMessage(value)
80    }
81}
82
83impl fmt::Display for Error {
84    #[inline]
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match self {
87            Self::InvalidMutlipartLength(len) => {
88                write!(f, "invalid multipart message length: {len} (expected 3)")
89            }
90            Self::InvalidTopic(len, topic) => {
91                write!(
92                    f,
93                    "invalid message topic '{}'{}",
94                    String::from_utf8_lossy(&topic[..min(*len, topic.len())]),
95                    if *len > TOPIC_MAX_LEN {
96                        " (truncated)"
97                    } else {
98                        ""
99                    }
100                )
101            }
102            Self::InvalidDataLength(len) => {
103                write!(f, "data too long ({len} > {DATA_MAX_LEN})")
104            }
105            Self::InvalidSequenceLength(len) => {
106                write!(
107                    f,
108                    "invalid sequence length: {len} (expected {SEQUENCE_LEN})"
109                )
110            }
111            Self::InvalidSequenceMessageLength(len) => {
112                write!(f, "invalid message length {len} of message type 'sequence'")
113            }
114            Self::InvalidSequenceMessageLabel(label) => {
115                write!(
116                    f,
117                    "invalid label '{}' (0x{:02x}) of message type 'sequence'",
118                    *label as char, label
119                )
120            }
121            Self::Invalid256BitHashLength(len) => {
122                write!(f, "invalid hash length: {len} (expected 32)")
123            }
124
125            Self::BitcoinDeserialization(e) => {
126                write!(f, "bitcoin consensus deserialization error: {e}")
127            }
128            Self::Zmq(e) => write!(f, "ZMQ Error: {e}"),
129            Self::MonitorMessage(err) => write!(f, "unable to parse monitor message: {err}"),
130        }
131    }
132}
133
134impl std::error::Error for Error {
135    #[inline]
136    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
137        Some(match self {
138            Self::BitcoinDeserialization(e) => e,
139            Self::Zmq(e) => e,
140            Self::MonitorMessage(e) => e,
141            Self::InvalidMutlipartLength(_)
142            | Self::InvalidTopic(_, _)
143            | Self::InvalidDataLength(_)
144            | Self::InvalidSequenceLength(_)
145            | Self::InvalidSequenceMessageLength(_)
146            | Self::InvalidSequenceMessageLabel(_)
147            | Self::Invalid256BitHashLength(_) => return None,
148        })
149    }
150}