1use serde::Serialize;
2use thiserror::Error;
3
4use super::ErrorCode;
5
6#[derive(Debug, Error, PartialEq, Clone, Serialize)]
9pub enum ConfErrReason {
10 #[error("core config > {0}")]
11 Core(String),
12 #[error("feature config error > {0}")]
13 Feature(String),
14 #[error("dynamic config error > {0}")]
15 Dynamic(String),
16}
17
18#[derive(Debug, Error, PartialEq, Clone, Serialize)]
31pub enum UvsReason {
32 #[error("validation error << {0}")]
35 ValidationError(String),
36
37 #[error("business logic error << {0}")]
39 BusinessError(String),
40
41 #[error("run rule error << {0}")]
43 RunRuleError(String),
44
45 #[error("not found error << {0}")]
47 NotFoundError(String),
48
49 #[error("permission error << {0}")]
51 PermissionError(String),
52
53 #[error("data error << {0}")]
56 DataError(String, Option<usize>),
57
58 #[error("system error << {0}")]
60 SystemError(String),
61
62 #[error("network error << {0}")]
64 NetworkError(String),
65
66 #[error("resource error << {0}")]
68 ResourceError(String),
69
70 #[error("timeout error << {0}")]
72 TimeoutError(String),
73
74 #[error("configuration error << {0}")]
77 ConfigError(ConfErrReason),
78
79 #[error("external service error << {0}")]
81 ExternalError(String),
82
83 #[error("BUG :logic error << {0}")]
85 LogicError(String),
86}
87
88impl UvsReason {
89 pub fn core_conf<S: Into<String>>(msg: S) -> Self {
91 Self::ConfigError(ConfErrReason::Core(msg.into()))
92 }
93
94 pub fn feature_conf<S: Into<String>>(msg: S) -> Self {
95 Self::ConfigError(ConfErrReason::Feature(msg.into()))
96 }
97
98 pub fn dynamic_conf<S: Into<String>>(msg: S) -> Self {
99 Self::ConfigError(ConfErrReason::Dynamic(msg.into()))
100 }
101
102 pub fn validation_error<S: Into<String>>(msg: S) -> Self {
104 Self::ValidationError(msg.into())
105 }
106
107 pub fn business_error<S: Into<String>>(msg: S) -> Self {
108 Self::BusinessError(msg.into())
109 }
110
111 pub fn rule_error<S: Into<String>>(msg: S) -> Self {
112 Self::RunRuleError(msg.into())
113 }
114
115 pub fn not_found_error<S: Into<String>>(msg: S) -> Self {
116 Self::NotFoundError(msg.into())
117 }
118
119 pub fn permission_error<S: Into<String>>(msg: S) -> Self {
120 Self::PermissionError(msg.into())
121 }
122
123 pub fn data_error<S: Into<String>>(msg: S, pos: Option<usize>) -> Self {
125 Self::DataError(msg.into(), pos)
126 }
127
128 pub fn system_error<S: Into<String>>(msg: S) -> Self {
129 Self::SystemError(msg.into())
130 }
131
132 pub fn network_error<S: Into<String>>(msg: S) -> Self {
133 Self::NetworkError(msg.into())
134 }
135
136 pub fn resource_error<S: Into<String>>(msg: S) -> Self {
137 Self::ResourceError(msg.into())
138 }
139
140 pub fn timeout_error<S: Into<String>>(msg: S) -> Self {
141 Self::TimeoutError(msg.into())
142 }
143
144 pub fn external_error<S: Into<String>>(msg: S) -> Self {
146 Self::ExternalError(msg.into())
147 }
148 pub fn logic_error<S: Into<String>>(msg: S) -> Self {
149 Self::LogicError(msg.into())
150 }
151}
152
153pub trait UvsConfFrom<S> {
156 fn from_conf(info: S) -> Self;
157}
158
159pub trait UvsDataFrom<S> {
160 fn from_data(info: S, pos: Option<usize>) -> Self;
161}
162
163pub trait UvsSysFrom<S> {
164 fn from_sys(info: S) -> Self;
165}
166
167pub trait UvsBizFrom<S> {
168 fn from_biz(info: S) -> Self;
169}
170pub trait UvsLogicFrom<S> {
171 fn from_logic(info: S) -> Self;
172}
173
174pub trait UvsResFrom<S> {
175 fn from_res(info: S) -> Self;
176}
177
178pub trait UvsNetFrom<S> {
179 fn from_net(info: S) -> Self;
180}
181
182pub trait UvsTimeoutFrom<S> {
183 fn from_timeout(info: S) -> Self;
184}
185
186pub trait UvsValidationFrom<S> {
188 fn from_validation(info: S) -> Self;
189}
190
191pub trait UvsNotFoundFrom<S> {
192 fn from_not_found(info: S) -> Self;
193}
194
195pub trait UvsPermissionFrom<S> {
196 fn from_permission(info: S) -> Self;
197}
198
199pub trait UvsExternalFrom<S> {
200 fn from_external(info: S) -> Self;
201}
202
203impl<T> UvsConfFrom<String> for T
206where
207 T: From<UvsReason>,
208{
209 fn from_conf(reason: String) -> Self {
210 T::from(UvsReason::core_conf(reason))
211 }
212}
213
214impl<T> UvsConfFrom<&str> for T
215where
216 T: From<UvsReason>,
217{
218 fn from_conf(reason: &str) -> Self {
219 T::from(UvsReason::core_conf(reason))
220 }
221}
222
223impl<T> UvsConfFrom<ConfErrReason> for T
224where
225 T: From<UvsReason>,
226{
227 fn from_conf(reason: ConfErrReason) -> Self {
228 T::from(UvsReason::ConfigError(reason))
229 }
230}
231
232impl<T> UvsDataFrom<String> for T
233where
234 T: From<UvsReason>,
235{
236 fn from_data(info: String, pos: Option<usize>) -> Self {
237 T::from(UvsReason::data_error(info, pos))
238 }
239}
240
241impl<T> UvsDataFrom<&str> for T
242where
243 T: From<UvsReason>,
244{
245 fn from_data(info: &str, pos: Option<usize>) -> Self {
246 T::from(UvsReason::data_error(info, pos))
247 }
248}
249
250impl<T> UvsSysFrom<String> for T
251where
252 T: From<UvsReason>,
253{
254 fn from_sys(info: String) -> Self {
255 T::from(UvsReason::system_error(info))
256 }
257}
258
259impl<T> UvsSysFrom<&str> for T
260where
261 T: From<UvsReason>,
262{
263 fn from_sys(info: &str) -> Self {
264 T::from(UvsReason::system_error(info))
265 }
266}
267
268impl<T> UvsBizFrom<String> for T
269where
270 T: From<UvsReason>,
271{
272 fn from_biz(info: String) -> Self {
273 T::from(UvsReason::business_error(info))
274 }
275}
276
277impl<T> UvsBizFrom<&str> for T
278where
279 T: From<UvsReason>,
280{
281 fn from_biz(info: &str) -> Self {
282 T::from(UvsReason::business_error(info))
283 }
284}
285
286impl<T> UvsResFrom<String> for T
287where
288 T: From<UvsReason>,
289{
290 fn from_res(info: String) -> Self {
291 T::from(UvsReason::resource_error(info))
292 }
293}
294
295impl<T> UvsResFrom<&str> for T
296where
297 T: From<UvsReason>,
298{
299 fn from_res(info: &str) -> Self {
300 T::from(UvsReason::resource_error(info))
301 }
302}
303
304impl<T> UvsNetFrom<String> for T
305where
306 T: From<UvsReason>,
307{
308 fn from_net(info: String) -> Self {
309 T::from(UvsReason::network_error(info)) }
311}
312
313impl<T> UvsNetFrom<&str> for T
314where
315 T: From<UvsReason>,
316{
317 fn from_net(info: &str) -> Self {
318 T::from(UvsReason::network_error(info)) }
320}
321
322impl<T> UvsTimeoutFrom<String> for T
323where
324 T: From<UvsReason>,
325{
326 fn from_timeout(info: String) -> Self {
327 T::from(UvsReason::timeout_error(info))
328 }
329}
330
331impl<T> UvsTimeoutFrom<&str> for T
332where
333 T: From<UvsReason>,
334{
335 fn from_timeout(info: &str) -> Self {
336 T::from(UvsReason::timeout_error(info))
337 }
338}
339
340impl<T> UvsValidationFrom<String> for T
342where
343 T: From<UvsReason>,
344{
345 fn from_validation(info: String) -> Self {
346 T::from(UvsReason::validation_error(info))
347 }
348}
349
350impl<T> UvsValidationFrom<&str> for T
351where
352 T: From<UvsReason>,
353{
354 fn from_validation(info: &str) -> Self {
355 T::from(UvsReason::validation_error(info))
356 }
357}
358
359impl<T> UvsNotFoundFrom<String> for T
360where
361 T: From<UvsReason>,
362{
363 fn from_not_found(info: String) -> Self {
364 T::from(UvsReason::not_found_error(info))
365 }
366}
367
368impl<T> UvsNotFoundFrom<&str> for T
369where
370 T: From<UvsReason>,
371{
372 fn from_not_found(info: &str) -> Self {
373 T::from(UvsReason::not_found_error(info))
374 }
375}
376
377impl<T> UvsPermissionFrom<String> for T
378where
379 T: From<UvsReason>,
380{
381 fn from_permission(info: String) -> Self {
382 T::from(UvsReason::permission_error(info))
383 }
384}
385
386impl<T> UvsPermissionFrom<&str> for T
387where
388 T: From<UvsReason>,
389{
390 fn from_permission(info: &str) -> Self {
391 T::from(UvsReason::permission_error(info))
392 }
393}
394
395impl<T> UvsExternalFrom<String> for T
396where
397 T: From<UvsReason>,
398{
399 fn from_external(info: String) -> Self {
400 T::from(UvsReason::external_error(info))
401 }
402}
403
404impl<T> UvsExternalFrom<&str> for T
405where
406 T: From<UvsReason>,
407{
408 fn from_external(info: &str) -> Self {
409 T::from(UvsReason::external_error(info))
410 }
411}
412
413impl<T> UvsLogicFrom<String> for T
414where
415 T: From<UvsReason>,
416{
417 fn from_logic(info: String) -> Self {
418 T::from(UvsReason::logic_error(info))
419 }
420}
421
422impl<T> UvsLogicFrom<&str> for T
423where
424 T: From<UvsReason>,
425{
426 fn from_logic(info: &str) -> Self {
427 T::from(UvsReason::logic_error(info))
428 }
429}
430
431impl ErrorCode for UvsReason {
432 fn error_code(&self) -> i32 {
433 match self {
434 UvsReason::ValidationError(_) => 100,
436 UvsReason::BusinessError(_) => 101,
437 UvsReason::NotFoundError(_) => 102,
438 UvsReason::PermissionError(_) => 103,
439 UvsReason::LogicError(_) => 104,
440 UvsReason::RunRuleError(_) => 105,
441
442 UvsReason::DataError(_, _) => 200,
444 UvsReason::SystemError(_) => 201,
445 UvsReason::NetworkError(_) => 202,
446 UvsReason::ResourceError(_) => 203,
447 UvsReason::TimeoutError(_) => 204,
448
449 UvsReason::ConfigError(_) => 300,
451 UvsReason::ExternalError(_) => 301,
452 }
453 }
454}
455
456impl UvsReason {
459 pub fn is_retryable(&self) -> bool {
462 match self {
463 UvsReason::NetworkError(_) => true,
465 UvsReason::TimeoutError(_) => true,
466 UvsReason::ResourceError(_) => true,
467 UvsReason::SystemError(_) => true,
468 UvsReason::ExternalError(_) => true,
469
470 UvsReason::ValidationError(_) => false,
472 UvsReason::BusinessError(_) => false,
473 UvsReason::RunRuleError(_) => false,
474 UvsReason::NotFoundError(_) => false,
475 UvsReason::PermissionError(_) => false,
476
477 UvsReason::ConfigError(_) => false,
479 UvsReason::DataError(_, _) => false,
480 UvsReason::LogicError(_) => false,
481 }
482 }
483
484 pub fn is_high_severity(&self) -> bool {
487 match self {
488 UvsReason::SystemError(_) => true,
490 UvsReason::ResourceError(_) => true,
491 UvsReason::ConfigError(_) => true,
492
493 _ => false,
495 }
496 }
497
498 pub fn category_name(&self) -> &'static str {
501 match self {
502 UvsReason::ValidationError(_) => "validation",
503 UvsReason::BusinessError(_) => "business",
504 UvsReason::RunRuleError(_) => "runrule",
505 UvsReason::NotFoundError(_) => "not_found",
506 UvsReason::PermissionError(_) => "permission",
507 UvsReason::DataError(_, _) => "data",
508 UvsReason::SystemError(_) => "system",
509 UvsReason::NetworkError(_) => "network",
510 UvsReason::ResourceError(_) => "resource",
511 UvsReason::TimeoutError(_) => "timeout",
512 UvsReason::ConfigError(_) => "config",
513 UvsReason::ExternalError(_) => "external",
514 UvsReason::LogicError(_) => "logic",
515 }
516 }
517}
518
519#[cfg(test)]
520mod tests {
521 use super::*;
522
523 #[test]
524 fn test_error_code_ranges() {
525 assert_eq!(UvsReason::validation_error("test").error_code(), 100);
527 assert_eq!(UvsReason::business_error("test").error_code(), 101);
528 assert_eq!(UvsReason::not_found_error("test").error_code(), 102);
529 assert_eq!(UvsReason::permission_error("test").error_code(), 103);
530
531 assert_eq!(UvsReason::data_error("test", None).error_code(), 200);
533 assert_eq!(UvsReason::system_error("test").error_code(), 201);
534 assert_eq!(UvsReason::network_error("test").error_code(), 202);
535 assert_eq!(UvsReason::resource_error("test").error_code(), 203);
536 assert_eq!(UvsReason::timeout_error("test").error_code(), 204);
537
538 assert_eq!(UvsReason::core_conf("test").error_code(), 300);
540 assert_eq!(UvsReason::external_error("test").error_code(), 301);
541 }
542
543 #[test]
544 fn test_retryable_errors() {
545 assert!(UvsReason::network_error("timeout").is_retryable());
546 assert!(UvsReason::timeout_error("request timeout").is_retryable());
547 assert!(!UvsReason::validation_error("invalid input").is_retryable());
548 assert!(!UvsReason::business_error("insufficient funds").is_retryable());
549 }
550
551 #[test]
552 fn test_high_severity_errors() {
553 assert!(UvsReason::system_error("disk full").is_high_severity());
554 assert!(UvsReason::resource_error("out of memory").is_high_severity());
555 assert!(!UvsReason::validation_error("bad format").is_high_severity());
556 assert!(!UvsReason::NotFoundError("user not found".into()).is_high_severity());
557 }
558
559 #[test]
560 fn test_category_names() {
561 assert_eq!(UvsReason::network_error("test").category_name(), "network");
562 assert_eq!(
563 UvsReason::business_error("test").category_name(),
564 "business"
565 );
566 assert_eq!(UvsReason::core_conf("test").category_name(), "config");
567 }
568
569 #[test]
570 fn test_trait_implementations() {
571 let reason: UvsReason = UvsReason::from_net("network error".to_string());
573 assert_eq!(reason.error_code(), 202);
574
575 let reason: UvsReason = UvsReason::from_validation("validation error".to_string());
576 assert_eq!(reason.error_code(), 100);
577
578 let reason: UvsReason = UvsReason::from_external("external error".to_string());
579 assert_eq!(reason.error_code(), 301);
580 }
581}