barter_data/
error.rs

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