wp_connector_api/errors/
source.rs1use derive_more::From;
2use orion_error::{ErrorCode, StructError, ToStructError, UvsReason};
3use serde::Serialize;
4use std::error::Error as StdError;
5use thiserror::Error;
6
7#[derive(Error, Debug, Clone, PartialEq, Serialize, From)]
8pub enum SourceReason {
9 #[error("not data")]
10 NotData,
11 #[error("eof")]
12 EOF,
13 #[error("supplier error : {0}")]
14 SupplierError(String),
15 #[from(skip)]
16 #[error("disconnected: {0}")]
17 Disconnect(String),
18 #[from(skip)]
19 #[error("{0}")]
20 Other(String),
21 #[error("{0}")]
22 Uvs(UvsReason),
23}
24
25impl ErrorCode for SourceReason {
26 fn error_code(&self) -> i32 {
27 match self {
28 SourceReason::NotData => 100, SourceReason::EOF => 101, SourceReason::Disconnect(_) => 503, SourceReason::SupplierError(_) => 500, SourceReason::Other(_) => 520, SourceReason::Uvs(r) => r.error_code(),
41 }
42 }
43}
44
45pub type SourceError = StructError<SourceReason>;
46pub type SourceResult<T> = Result<T, StructError<SourceReason>>;
47
48impl SourceReason {
49 pub fn err(self) -> SourceError {
50 self.to_err()
51 }
52
53 pub fn err_detail<S: Into<String>>(self, detail: S) -> SourceError {
54 self.to_err().with_detail(detail.into())
55 }
56
57 pub fn err_source<E>(self, source: E) -> SourceError
58 where
59 E: StdError + Send + Sync + 'static,
60 {
61 self.to_err().with_source(source)
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn source_reason_error_codes() {
71 assert_eq!(SourceReason::NotData.error_code(), 100);
73 assert_eq!(SourceReason::EOF.error_code(), 101);
74
75 assert_eq!(
77 SourceReason::Disconnect("conn lost".into()).error_code(),
78 503
79 );
80
81 assert_eq!(
83 SourceReason::SupplierError("upstream".into()).error_code(),
84 500
85 );
86 assert_eq!(SourceReason::Other("misc".into()).error_code(), 520);
87 }
88
89 #[test]
90 fn source_reason_error_codes_are_distinct() {
91 let codes = vec![
92 SourceReason::NotData.error_code(),
93 SourceReason::EOF.error_code(),
94 SourceReason::Disconnect("x".into()).error_code(),
95 SourceReason::SupplierError("x".into()).error_code(),
96 SourceReason::Other("x".into()).error_code(),
97 ];
98 let mut unique = codes.clone();
100 unique.sort();
101 unique.dedup();
102 assert_eq!(codes.len(), unique.len(), "error codes should be distinct");
103 }
104
105 #[test]
106 fn source_reason_informational_codes_are_below_200() {
107 assert!(SourceReason::NotData.error_code() < 200);
108 assert!(SourceReason::EOF.error_code() < 200);
109 }
110
111 #[test]
112 fn source_reason_retryable_codes_are_5xx() {
113 let code = SourceReason::Disconnect("x".into()).error_code();
114 assert!((500..600).contains(&code));
115 }
116
117 #[test]
118 fn source_reason_err_detail_sets_detail() {
119 let err = SourceReason::Other("boom".into()).err_detail("ctx");
120 assert_eq!(err.detail().as_deref(), Some("ctx"));
121 }
122
123 #[test]
124 fn source_reason_err_source_preserves_source_message() {
125 let err = SourceReason::Disconnect("read failed".into())
126 .err_source(std::io::Error::other("disk gone"));
127 assert!(err.to_string().contains("disk gone"));
128 }
129}