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 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 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 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#[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 .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}