api_response/error_code/
mod.rs

1mod errpath;
2mod errtype;
3pub mod ety_grpc;
4pub mod tally;
5
6use std::{
7    fmt::Display,
8    ops::{Add, BitOr},
9    thread::LocalKey,
10};
11
12pub use errpath::*;
13pub use errtype::*;
14use getset2::Getset2;
15use serde::{Deserialize, Serialize};
16
17use crate::ApiError;
18
19#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize)]
20#[non_exhaustive]
21pub struct ErrDecl {
22    pub err_type: ErrType,
23    pub err_path: ErrPath,
24}
25
26impl ErrDecl {
27    #[inline]
28    pub const fn new(err_type: ErrType, err_path: ErrPath) -> Self {
29        Self { err_type, err_path }
30    }
31    #[inline]
32    pub const fn err_flag(&self) -> u32 {
33        self.err_type.flag() as u32
34    }
35    #[inline]
36    pub const fn err_path_flag(&self) -> u32 {
37        self.err_path.path_flag()
38    }
39    #[inline]
40    pub const fn text(&self) -> &'static str {
41        self.err_type.text()
42    }
43    pub const fn err_type(&self) -> &ErrType {
44        &self.err_type
45    }
46    pub const fn err_path(&self) -> &ErrPath {
47        &self.err_path
48    }
49    #[inline(always)]
50    pub const fn extract(&self) -> ErrBrief {
51        ErrBrief::new(self.err_type, &self.err_path)
52    }
53    #[inline(always)]
54    pub fn api_error(&self) -> ApiError {
55        self.extract().api_error()
56    }
57}
58
59impl Display for ErrDecl {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(f, "{}, {}", self.extract(), self.err_path)
62    }
63}
64
65#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Getset2, Serialize, Deserialize)]
66#[getset2(get_copy(pub, const))]
67#[non_exhaustive]
68pub struct ErrBrief {
69    message: &'static str,
70    /// The value range of the code is from 1000000000 to 4293999999 inclusive.
71    code: u32,
72}
73impl Display for ErrBrief {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        if self.message.is_empty() {
76            write!(f, "<no message> ErrCode({})", self.code)
77        } else {
78            write!(f, "{} ErrCode({})", self.message, self.code)
79        }
80    }
81}
82impl ErrBrief {
83    #[inline(always)]
84    pub const fn new(err_type: ErrType, err_path: &ErrPath) -> Self {
85        Self {
86            message: err_type.text(),
87            code: (err_type.flag() as u32 * 1000000) + err_path.path_flag(),
88        }
89    }
90    #[inline(always)]
91    pub fn api_error(&self) -> ApiError {
92        ApiError {
93            code: self.code,
94            message: self.message.to_owned(),
95            details: None,
96            source: None,
97        }
98    }
99}
100
101impl BitOr<&'static str> for ErrType {
102    type Output = ErrType;
103
104    #[inline(always)]
105    fn bitor(self, rhs: &'static str) -> Self::Output {
106        self.with_text(rhs)
107    }
108}
109
110impl Add<ErrPath> for ErrType {
111    type Output = ErrDecl;
112
113    #[inline(always)]
114    fn add(self, rhs: ErrPath) -> Self::Output {
115        self.declare(rhs)
116    }
117}
118impl Add<&ErrPath> for ErrType {
119    type Output = ErrDecl;
120
121    #[inline(always)]
122    fn add(self, rhs: &ErrPath) -> Self::Output {
123        self.declare(*rhs)
124    }
125}
126impl Add<&'static LocalKey<ErrPath>> for ErrType {
127    type Output = ErrDecl;
128
129    #[inline(always)]
130    fn add(self, rhs: &'static LocalKey<ErrPath>) -> Self::Output {
131        rhs.with(|v| self.declare(*v))
132    }
133}
134
135impl BitOr<&ErrPath> for ErrType {
136    type Output = ApiError;
137
138    #[inline(always)]
139    fn bitor(self, rhs: &ErrPath) -> Self::Output {
140        self.api_error(rhs)
141    }
142}
143impl BitOr<&'static LocalKey<ErrPath>> for ErrType {
144    type Output = ApiError;
145
146    #[inline(always)]
147    fn bitor(self, rhs: &'static LocalKey<ErrPath>) -> Self::Output {
148        rhs.with(|v| self.api_error(v))
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use std::cell::LazyCell;
155
156    use super::{ErrDecl, ErrPath, ErrPathParent, ErrPathRoot, ErrType};
157    use crate::ApiError;
158
159    #[test]
160    fn display() {
161        const ET: ErrType = ErrType::T1100("The operation was cancelled.");
162        const EP_LV1: ErrPathRoot = ErrPathRoot::X00("product");
163        const EP_LV2: ErrPathParent = EP_LV1.Y01("system");
164        const EP_LV3: ErrPath = EP_LV2.Z20("module");
165        const EC: ErrDecl = ErrDecl::new(ET, EP_LV3);
166        assert_eq!(
167            "The operation was cancelled. ErrCode(1100000120), X00(product)/Y01(system)/Z20(module)",
168            EC.to_string()
169        );
170
171        let api_error: ApiError = ET | &EP_LV3;
172        assert_eq!(EC.api_error().code(), api_error.code());
173        let mp: LazyCell<ErrPath> = LazyCell::new(|| EP_LV3);
174        let api_error: ApiError = ET | &*mp;
175        assert_eq!(EC.api_error().code(), api_error.code());
176    }
177
178    #[test]
179    fn min_max_code() {
180        let min_code: ErrDecl = ErrType::T1000("") + ErrPathRoot::X00("").Y00("").Z00("");
181        assert_eq!(
182            "<no message> ErrCode(1000000000), X00()/Y00()/Z00()",
183            min_code.to_string()
184        );
185
186        let max_code: ErrDecl = ErrType::T4293("") + ErrPathRoot::X99("").Y99("").Z99("");
187        assert_eq!(
188            "<no message> ErrCode(4293999999), X99()/Y99()/Z99()",
189            max_code.to_string()
190        );
191    }
192}