barter_data_sniper/
error.rs

1use crate::subscription::SubKind;
2use barter_instrument_copy::exchange::ExchangeId;
3use barter_integration_copy::{error::SocketError, subscription::SubscriptionId};
4use thiserror::Error;
5
6/// All errors generated in `barter-data`.
7#[derive(Debug, Clone, Error)]
8pub enum DataError {
9    #[error("failed to initialise reconnecting MarketStream due to empty subscriptions")]
10    SubscriptionsEmpty,
11
12    #[error("unsupported DynamicStreams Subscription SubKind: {0}")]
13    UnsupportedSubKind(SubKind),
14
15    #[error("initial snapshot missing for: {0}")]
16    InitialSnapshotMissing(SubscriptionId),
17
18    #[error("initial snapshot invalid: {0}")]
19    InitialSnapshotInvalid(&'static str),
20
21    #[error("SocketError: {0}")]
22    Socket(String),
23
24    #[error("unsupported dynamic Subscription for exchange: {exchange}, kind: {sub_kind}")]
25    Unsupported {
26        exchange: ExchangeId,
27        sub_kind: SubKind,
28    },
29
30    #[error(
31        "\
32        InvalidSequence: first_update_id {first_update_id} does not follow on from the \
33        prev_last_update_id {prev_last_update_id} \
34    "
35    )]
36    InvalidSequence {
37        prev_last_update_id: u64,
38        first_update_id: u64,
39    },
40}
41
42impl DataError {
43    /// Determine if an error requires a [`MarketStream`](super::MarketStream) to re-initialise.
44    #[allow(clippy::match_like_matches_macro)]
45    pub fn is_terminal(&self) -> bool {
46        match self {
47            DataError::InvalidSequence { .. } => true,
48            _ => false,
49        }
50    }
51}
52
53impl From<SocketError> for DataError {
54    fn from(value: SocketError) -> Self {
55        Self::Socket(value.to_string())
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn test_data_error_is_terminal() {
65        struct TestCase {
66            input: DataError,
67            expected: bool,
68        }
69
70        let tests = vec![
71            TestCase {
72                // TC0: is terminal w/ DataError::InvalidSequence
73                input: DataError::InvalidSequence {
74                    prev_last_update_id: 0,
75                    first_update_id: 0,
76                },
77                expected: true,
78            },
79            TestCase {
80                // TC1: is not terminal w/ DataError::Socket
81                input: DataError::from(SocketError::Sink),
82                expected: false,
83            },
84        ];
85
86        for (index, test) in tests.into_iter().enumerate() {
87            let actual = test.input.is_terminal();
88            assert_eq!(actual, test.expected, "TC{} failed", index);
89        }
90    }
91}