api_response/error_code/
errpath.rs

1use api_response_macros::ErrPathConstructor;
2
3#[derive(
4    Debug,
5    Clone,
6    Copy,
7    Eq,
8    PartialEq,
9    PartialOrd,
10    Ord,
11    Hash,
12    getset2::Getset2,
13    serde::Serialize,
14    serde::Deserialize,
15    ErrPathConstructor
16)]
17#[getset2(get_copy(pub, const))]
18#[non_exhaustive]
19pub struct ErrPathRoot {
20    name: &'static str,
21    /// The value range of the flag is from 0 to 99 inclusive.
22    flag: u8,
23}
24#[derive(
25    Debug,
26    Clone,
27    Copy,
28    Eq,
29    PartialEq,
30    PartialOrd,
31    Ord,
32    Hash,
33    getset2::Getset2,
34    serde::Serialize,
35    serde::Deserialize,
36    ErrPathConstructor
37)]
38#[getset2(get_copy(pub, const))]
39#[non_exhaustive]
40pub struct ErrPathParent {
41    root: ErrPathRoot,
42    name: &'static str,
43    /// The value range of the flag is from 0 to 99 inclusive.
44    flag: u8,
45}
46
47#[derive(
48    Debug,
49    Clone,
50    Copy,
51    Eq,
52    PartialEq,
53    PartialOrd,
54    Ord,
55    Hash,
56    getset2::Getset2,
57    serde::Serialize,
58    serde::Deserialize
59)]
60#[getset2(get_copy(pub, const))]
61#[non_exhaustive]
62pub struct ErrPath {
63    parent: ErrPathParent,
64    name: &'static str,
65    /// The value range of the flag is from 0 to 99 inclusive.
66    flag: u8,
67}
68impl ErrPathRoot {
69    #[inline]
70    pub const fn default() -> Self {
71        Self { name: "", flag: 0 }
72    }
73    #[inline]
74    pub fn path(&self) -> String {
75        self.to_string()
76    }
77    #[inline]
78    pub const fn path_flag(&self) -> u32 {
79        self.flag as u32
80    }
81    pub fn try_from<T: TryInto<u8>>(flag: T, name: &'static str) -> Result<Self, InvalidErrPathFlag> {
82        let x: u8 = flag.try_into().map_err(|_| InvalidErrPathFlag::new())?;
83        if x > 99 {
84            Err(InvalidErrPathFlag::new())
85        } else {
86            Ok(Self { name, flag: x })
87        }
88    }
89    pub fn try_to_child<T: TryInto<u8>>(
90        self,
91        child_flag: T,
92        child_name: &'static str,
93    ) -> Result<ErrPathParent, InvalidErrPathFlag> {
94        let x: u8 = child_flag.try_into().map_err(|_| InvalidErrPathFlag::new())?;
95        if x > 99 {
96            Err(InvalidErrPathFlag::new())
97        } else {
98            Ok(ErrPathParent {
99                root: self,
100                name: child_name,
101                flag: x,
102            })
103        }
104    }
105}
106impl ErrPathParent {
107    #[inline]
108    pub const fn default() -> Self {
109        Self {
110            root: ErrPathRoot::default(),
111            name: "",
112            flag: 0,
113        }
114    }
115    #[inline]
116    pub fn path(&self) -> String {
117        self.to_string()
118    }
119    #[inline]
120    pub const fn path_flag(&self) -> u32 {
121        (self.root.path_flag() * 100) + self.flag as u32
122    }
123    pub fn try_to_child<T: TryInto<u8>>(
124        self,
125        child_flag: T,
126        child_name: &'static str,
127    ) -> Result<ErrPath, InvalidErrPathFlag> {
128        let x: u8 = child_flag.try_into().map_err(|_| InvalidErrPathFlag::new())?;
129        if x > 99 {
130            Err(InvalidErrPathFlag::new())
131        } else {
132            Ok(ErrPath {
133                parent: self,
134                name: child_name,
135                flag: x,
136            })
137        }
138    }
139}
140impl ErrPath {
141    #[inline]
142    pub const fn default() -> Self {
143        Self {
144            parent: ErrPathParent::default(),
145            name: "",
146            flag: 0,
147        }
148    }
149    #[inline]
150    pub fn path(&self) -> String {
151        self.to_string()
152    }
153    #[inline]
154    pub const fn path_flag(&self) -> u32 {
155        (self.parent.path_flag() * 100) + self.flag as u32
156    }
157}
158
159impl std::fmt::Display for ErrPathRoot {
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        write!(f, "X{:02}({})", self.flag, self.name)
162    }
163}
164impl std::fmt::Display for ErrPathParent {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        write!(f, "{}/Y{:02}({})", self.root, self.flag, self.name)
167    }
168}
169impl std::fmt::Display for ErrPath {
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        write!(f, "{}/Z{:02}({})", self.parent, self.flag, self.name)
172    }
173}
174
175/// A possible error value when converting a `ErrType` from a digit
176///
177/// This error indicates that the supplied input was not a valid digit, was
178/// less than 1000, or was greater than 4293.
179#[derive(PartialEq)]
180pub struct InvalidErrPathFlag {
181    _priv: (),
182}
183impl InvalidErrPathFlag {
184    const fn new() -> Self {
185        Self { _priv: () }
186    }
187}
188impl std::fmt::Debug for InvalidErrPathFlag {
189    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
190        f.debug_struct("InvalidErrPathFlag")
191            // skip _priv noise
192            .finish()
193    }
194}
195
196impl std::fmt::Display for InvalidErrPathFlag {
197    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198        f.write_str("invalid error path flag")
199    }
200}
201
202impl std::error::Error for InvalidErrPathFlag {}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207
208    #[test]
209    fn useage() {
210        const ERR_PATH_ROOT: ErrPathRoot = ErrPathRoot::X99("name1");
211        const ERR_PATH_PARENT: ErrPathParent = ERR_PATH_ROOT.Y01("name2");
212        const ERR_PATH: ErrPath = ERR_PATH_PARENT.Z20("name3");
213
214        assert_eq!("X99(name1)", ERR_PATH_ROOT.path());
215        assert_eq!("X99(name1)/Y01(name2)", ERR_PATH_PARENT.path());
216        assert_eq!("X99(name1)/Y01(name2)/Z20(name3)", ERR_PATH.path());
217    }
218    #[test]
219    fn convert() {
220        assert_eq!(Ok(ErrPathRoot::X00("")), ErrPathRoot::try_from(0, ""));
221        assert_eq!(Ok(ErrPathRoot::X99("")), ErrPathRoot::try_from(99, ""));
222        assert_eq!(Err(InvalidErrPathFlag::new()), ErrPathRoot::try_from(100, ""));
223        assert_eq!(
224            Ok(ErrPathRoot::X00("").Y00("")),
225            ErrPathRoot::X00("").try_to_child(0, "")
226        );
227        assert_eq!(
228            Ok(ErrPathRoot::X99("").Y99("")),
229            ErrPathRoot::X99("").try_to_child(99, "")
230        );
231        assert_eq!(
232            Err(InvalidErrPathFlag::new()),
233            ErrPathRoot::X99("").try_to_child(100, "")
234        );
235        assert_eq!(
236            Ok(ErrPathRoot::X00("").Y00("").Z00("")),
237            ErrPathRoot::X00("").Y00("").try_to_child(0, "")
238        );
239        assert_eq!(
240            Ok(ErrPathRoot::X99("").Y99("").Z99("")),
241            ErrPathRoot::X99("").Y99("").try_to_child(99, "")
242        );
243    }
244}