1use crate::config_error::{ConfCore, ConfError, ConfReason};
2use crate::parse_error::{OMLCodeError, OMLCodeReason};
3use derive_more::From;
4
5use orion_error::{OrionError, StructError, UnifiedReason};
6use orion_sec::OrionSecReason;
7use serde::Serialize;
8use thiserror::Error;
9
10pub type RunError = StructError<RunReason>;
11pub type RunResult<T> = Result<T, RunError>;
12
13#[derive(Debug, Error, PartialEq, Serialize, From)]
14pub enum DistFocus {
15 #[error("sink error : {0}")]
16 SinkError(String),
17 #[error("stg-ctrl")]
18 StgCtrl,
19}
20#[derive(Debug, Error, PartialEq, Serialize)]
21pub enum SourceFocus {
22 #[error("no data")]
23 NoData,
24 #[error("eof")]
25 Eof,
26 #[error("supplier error : {0}")]
27 SupplierError(String),
28 #[error("other : {0}")]
29 Other(String),
30 #[from(skip)]
31 #[error("disconnect : {0}")]
32 Disconnect(String),
33}
34
35#[derive(Debug, PartialEq, Serialize, From, OrionError)]
36pub enum RunReason {
37 #[orion_error(identity = "biz.dist")]
38 Dist(DistFocus),
39 #[orion_error(identity = "biz.source")]
40 Source(SourceFocus),
41 #[orion_error(transparent)]
42 Uvs(UnifiedReason),
43}
44
45impl From<ConfReason<ConfCore>> for RunReason {
46 fn from(_: ConfReason<ConfCore>) -> Self {
47 Self::core_conf()
48 }
49}
50impl From<OMLCodeReason> for RunReason {
51 fn from(_: OMLCodeReason) -> Self {
52 Self::core_conf()
53 }
54}
55
56pub trait IntoRunError {
64 fn into_run_err(self) -> RunError;
65}
66
67impl IntoRunError for OMLCodeError {
68 fn into_run_err(self) -> RunError {
69 let detail = match self.reason() {
70 OMLCodeReason::Syntax(s) => s.clone(),
71 OMLCodeReason::NotFound(s) => s.clone(),
72 OMLCodeReason::Uvs(_) => self.to_string(),
73 };
74 RunReason::core_conf()
75 .to_err()
76 .with_detail(detail)
77 .with_source(self)
78 }
79}
80
81impl IntoRunError for ConfError {
82 fn into_run_err(self) -> RunError {
83 let detail = match self.reason() {
85 ConfReason::Syntax(s) => s.clone(),
86 ConfReason::NotFound(s) => s.clone(),
87 ConfReason::Uvs(_) | ConfReason::_Take(_) => self.to_string(),
88 };
89 RunReason::core_conf()
90 .to_err()
91 .with_detail(detail)
92 .with_source(self)
93 }
94}
95
96impl IntoRunError for orion_conf::OrionConfError {
97 fn into_run_err(self) -> RunError {
98 let detail = match self.reason() {
101 orion_conf::ConfIOReason::Other(s) => s.clone(),
102 _ => self.to_string(),
103 };
104 RunReason::core_conf()
105 .to_err()
106 .with_detail(detail)
107 .with_source(self)
108 }
109}
110
111impl From<OrionSecReason> for RunReason {
112 fn from(value: OrionSecReason) -> Self {
113 match value {
114 OrionSecReason::Sec(_) => Self::core_conf(),
115 OrionSecReason::General(uvs_reason) => Self::Uvs(uvs_reason),
116 }
117 }
118}
119
120pub trait RunErrorOwe<T> {
121 fn owe_sink(self) -> RunResult<T>;
122 fn owe_source(self) -> RunResult<T>;
123}
124
125impl<T, E> RunErrorOwe<T> for Result<T, E>
126where
127 E: std::fmt::Display,
128{
129 fn owe_sink(self) -> RunResult<T> {
130 match self {
131 Ok(v) => Ok(v),
132 Err(e) => Err(RunReason::Dist(DistFocus::SinkError("sink fail".into()))
133 .to_err()
134 .with_detail(e.to_string())),
135 }
136 }
137 fn owe_source(self) -> RunResult<T> {
138 match self {
139 Ok(v) => Ok(v),
140 Err(e) => Err(
141 RunReason::Source(SourceFocus::SupplierError("source fail".into()))
142 .to_err()
143 .with_detail(e.to_string()),
144 ),
145 }
146 }
147}
148
149impl From<SourceReason> for RunReason {
163 fn from(e: SourceReason) -> Self {
164 match e {
165 SourceReason::NotData => Self::Source(SourceFocus::NoData),
166 SourceReason::EOF => Self::Source(SourceFocus::Eof),
167 SourceReason::SupplierError => Self::Source(SourceFocus::SupplierError(String::new())),
168 SourceReason::Disconnect => Self::Source(SourceFocus::Disconnect(String::new())),
169 SourceReason::Other => Self::Source(SourceFocus::Other(String::new())),
170 SourceReason::Uvs(uvs) => Self::Uvs(uvs),
171 }
172 }
174}
175
176impl From<SinkReason> for RunReason {
177 fn from(e: SinkReason) -> Self {
178 match e {
179 SinkReason::Sink => Self::Dist(DistFocus::SinkError(String::new())),
180 SinkReason::Mock => Self::Dist(DistFocus::StgCtrl),
182 SinkReason::StgCtrl => Self::Dist(DistFocus::StgCtrl),
183 SinkReason::Uvs(uvs) => Self::Uvs(uvs),
184 }
185 }
186}
187use orion_conf::ToStructError as _;
188use orion_conf::error::ConfIOReason;
189use wp_connector_api::{SinkReason, SourceReason};
190impl From<ConfIOReason> for RunReason {
191 fn from(value: ConfIOReason) -> Self {
192 match value {
193 ConfIOReason::Other(_) => RunReason::core_conf(),
194 ConfIOReason::General(uvs) => RunReason::Uvs(uvs),
195 ConfIOReason::NoFormatEnabled => RunReason::core_conf(),
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use crate::parse_error::OMLCodeReason;
204
205 #[test]
206 fn into_run_err_preserves_syntax_detail() {
207 let err = OMLCodeReason::Syntax(":oml code parse fail!\n[where]: line 5".into()).to_err();
208 let run = err.into_run_err();
209 assert!(
210 run.detail()
211 .as_deref()
212 .is_some_and(|d| d.contains("line 5")),
213 "detail should carry the OML inner message, got {:?}",
214 run.detail()
215 );
216 }
217
218 #[test]
219 fn into_run_err_keeps_config_reason() {
220 let err = OMLCodeReason::NotFound("missing file".into()).to_err();
221 let run = err.into_run_err();
222 assert!(
223 format!("{}", run.reason()).contains("config"),
224 "reason should stay a config error, got {}",
225 run.reason()
226 );
227 }
228
229 #[test]
230 fn conf_into_run_err_preserves_syntax_detail() {
231 let err = ConfReason::<ConfCore>::Syntax("invalid [sources] block".into()).to_err();
232 let run = err.into_run_err();
233 assert!(
234 run.detail()
235 .as_deref()
236 .is_some_and(|d| d.contains("invalid [sources] block")),
237 "ConfError detail should carry the inner message, got {:?}",
238 run.detail()
239 );
240 }
241
242 #[test]
243 fn conf_io_into_run_err_preserves_other_detail() {
244 use orion_conf::ConfIOReason;
245 let err = ConfIOReason::Other("expected `]` at line 2".into()).to_err();
246 let run = err.into_run_err();
247 assert!(
248 run.detail()
249 .as_deref()
250 .is_some_and(|d| d.contains("expected `]`")),
251 "ConfIOError detail should carry the toml error, got {:?}",
252 run.detail()
253 );
254 }
255}