wp_connector_api/errors/
sink.rs1use derive_more::From;
2use orion_error::conversion::ToStructError;
3use orion_error::{OrionError, StructError, UvsReason};
4use serde::Serialize;
5use std::error::Error as StdError;
6use std::sync::mpsc::SendError;
7
8#[derive(Debug, PartialEq, Serialize, From, OrionError)]
9pub enum SinkReason {
10 #[orion_error(identity = "biz.sink")]
11 Sink(String),
12 #[orion_error(identity = "biz.sink_mock", message = "set mock error")]
13 Mock,
14 #[orion_error(identity = "biz.sink_stg_ctrl", message = "stg ctrl error")]
15 StgCtrl,
16 #[orion_error(transparent)]
17 Uvs(UvsReason),
18}
19
20pub type SinkError = StructError<SinkReason>;
21
22pub trait ReasonSummary {
23 fn summary(&self) -> String;
24}
25
26impl<T> From<SendError<T>> for SinkReason
27where
28 T: ReasonSummary,
29{
30 fn from(err: SendError<T>) -> Self {
31 SinkReason::Sink(format!("send error: {}", err.0.summary()))
32 }
33}
34
35impl From<anyhow::Error> for SinkReason {
36 fn from(e: anyhow::Error) -> Self {
37 SinkReason::Sink(format!("{}", e))
38 }
39}
40
41pub type SinkResult<T> = Result<T, SinkError>;
42
43impl SinkReason {
44 pub fn sink<S: Into<String>>(msg: S) -> Self {
45 SinkReason::Sink(msg.into())
46 }
47
48 pub fn err(self) -> SinkError {
49 self.to_err()
50 }
51
52 pub fn err_detail<S: Into<String>>(self, detail: S) -> SinkError {
53 self.to_err().with_detail(detail.into())
54 }
55
56 pub fn err_source<E>(self, source: E) -> SinkError
57 where
58 E: StdError + Send + Sync + 'static,
59 {
60 self.to_err().with_source(source)
61 }
62}
63
64pub trait SinkErrorOwe<T> {
65 fn owe_sink<S: Into<String>>(self, msg: S) -> Result<T, StructError<SinkReason>>;
66}
67
68impl<T, E> SinkErrorOwe<T> for Result<T, E>
69where
70 E: std::fmt::Display,
71{
72 fn owe_sink<S: Into<String>>(self, msg: S) -> Result<T, StructError<SinkReason>> {
73 match self {
74 Ok(v) => Ok(v),
75 Err(e) => {
76 Err(StructError::from(SinkReason::Sink(msg.into())).with_detail(e.to_string()))
77 }
78 }
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use std::sync::mpsc;
86
87 #[derive(Clone)]
88 struct Summary(&'static str);
89
90 impl ReasonSummary for Summary {
91 fn summary(&self) -> String {
92 self.0.into()
93 }
94 }
95
96 #[test]
97 fn sink_reason_from_send_error_uses_inner_summary() {
98 let (tx, rx) = mpsc::channel();
99 drop(rx);
100 let err = tx.send(Summary("queue overflow")).unwrap_err();
101 let reason = SinkReason::from(err);
102 match reason {
103 SinkReason::Sink(msg) => assert!(msg.contains("queue overflow")),
104 other => panic!("unexpected reason: {other:?}"),
105 }
106 }
107
108 #[test]
109 fn sink_error_owe_wraps_displayable_error() {
110 let failing: Result<(), &str> = Err("io timeout");
111 let err = failing.owe_sink("flush failed").unwrap_err();
112 match err.reason() {
113 SinkReason::Sink(msg) => assert_eq!(msg, "flush failed"),
114 other => panic!("unexpected reason: {other:?}"),
115 }
116 let detail = err.detail();
117 assert_eq!(detail.as_ref().map(|s| s.as_str()), Some("io timeout"));
118 }
119
120 #[test]
121 fn sink_reason_err_detail_sets_detail() {
122 let err = SinkReason::sink("flush failed").err_detail("io timeout");
123 assert_eq!(err.detail().as_deref(), Some("io timeout"));
124 }
125
126 #[test]
127 fn sink_reason_err_source_preserves_source_message() {
128 let err = SinkReason::sink("udp send failed").err_source(std::io::Error::other("no route"));
129 assert!(err.to_string().contains("no route"));
130 }
131}