wp_connector_api/errors/
source.rs

1use derive_more::From;
2use orion_error::{ErrorCode, StructError, UvsReason};
3use serde::Serialize;
4use thiserror::Error;
5
6#[derive(Error, Debug, Clone, PartialEq, Serialize, From)]
7pub enum SourceReason {
8    #[error("not data")]
9    NotData,
10    #[error("eof")]
11    EOF,
12    #[error("supplier error : {0}")]
13    SupplierError(String),
14    #[from(skip)]
15    #[error("disconnected: {0}")]
16    Disconnect(String),
17    #[from(skip)]
18    #[error("{0}")]
19    Other(String),
20    #[error("{0}")]
21    Uvs(UvsReason),
22}
23
24impl ErrorCode for SourceReason {
25    fn error_code(&self) -> i32 {
26        match self {
27            // Informational: normal conditions
28            SourceReason::NotData => 100, // Temporary no data available
29            SourceReason::EOF => 101,     // End of data stream
30
31            // Retryable errors
32            SourceReason::Disconnect(_) => 503, // Connection lost, can retry
33
34            // Internal/supplier errors
35            SourceReason::SupplierError(_) => 500, // Upstream supplier error
36            SourceReason::Other(_) => 520,         // Unclassified error
37
38            // Delegate to wrapped reason
39            SourceReason::Uvs(r) => r.error_code(),
40        }
41    }
42}
43
44pub type SourceError = StructError<SourceReason>;
45pub type SourceResult<T> = Result<T, StructError<SourceReason>>;
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn source_reason_error_codes() {
53        // Informational codes (1xx)
54        assert_eq!(SourceReason::NotData.error_code(), 100);
55        assert_eq!(SourceReason::EOF.error_code(), 101);
56
57        // Retryable codes (5xx with specific meaning)
58        assert_eq!(
59            SourceReason::Disconnect("conn lost".into()).error_code(),
60            503
61        );
62
63        // Internal errors (5xx)
64        assert_eq!(
65            SourceReason::SupplierError("upstream".into()).error_code(),
66            500
67        );
68        assert_eq!(SourceReason::Other("misc".into()).error_code(), 520);
69    }
70
71    #[test]
72    fn source_reason_error_codes_are_distinct() {
73        let codes = vec![
74            SourceReason::NotData.error_code(),
75            SourceReason::EOF.error_code(),
76            SourceReason::Disconnect("x".into()).error_code(),
77            SourceReason::SupplierError("x".into()).error_code(),
78            SourceReason::Other("x".into()).error_code(),
79        ];
80        // Verify all codes are different
81        let mut unique = codes.clone();
82        unique.sort();
83        unique.dedup();
84        assert_eq!(codes.len(), unique.len(), "error codes should be distinct");
85    }
86
87    #[test]
88    fn source_reason_informational_codes_are_below_200() {
89        assert!(SourceReason::NotData.error_code() < 200);
90        assert!(SourceReason::EOF.error_code() < 200);
91    }
92
93    #[test]
94    fn source_reason_retryable_codes_are_5xx() {
95        let code = SourceReason::Disconnect("x".into()).error_code();
96        assert!((500..600).contains(&code));
97    }
98}