ocpi_kit/transport/
status.rs1use core::fmt;
17
18use serde::{Deserialize, Serialize};
19
20#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
36#[serde(transparent)]
37#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
38pub struct StatusCode(u16);
39
40impl StatusCode {
41 pub const SUCCESS: Self = Self(1000);
44
45 pub const CLIENT_ERROR: Self = Self(2000);
48 pub const INVALID_PARAMETERS: Self = Self(2001);
50 pub const NOT_ENOUGH_INFORMATION: Self = Self(2002);
52 pub const UNKNOWN_LOCATION: Self = Self(2003);
54 pub const UNKNOWN_TOKEN: Self = Self(2004);
56
57 pub const SERVER_ERROR: Self = Self(3000);
60 pub const UNABLE_TO_USE_CLIENT_API: Self = Self(3001);
62 pub const UNSUPPORTED_VERSION: Self = Self(3002);
64 pub const NO_MATCHING_ENDPOINTS: Self = Self(3003);
66
67 pub const HUB_ERROR: Self = Self(4000);
70 pub const UNKNOWN_RECEIVER: Self = Self(4001);
72 pub const TIMEOUT_ON_FORWARDED_REQUEST: Self = Self(4002);
74 pub const CONNECTION_PROBLEM: Self = Self(4003);
76
77 #[must_use]
79 pub const fn new(code: u16) -> Self {
80 Self(code)
81 }
82
83 #[must_use]
85 pub const fn get(self) -> u16 {
86 self.0
87 }
88
89 #[must_use]
91 pub const fn class(self) -> StatusClass {
92 match self.0 {
93 1000..=1999 => StatusClass::Success,
94 2000..=2999 => StatusClass::ClientError,
95 3000..=3999 => StatusClass::ServerError,
96 4000..=4999 => StatusClass::HubError,
97 _ => StatusClass::Unknown,
98 }
99 }
100
101 #[must_use]
107 pub const fn is_success(self) -> bool {
108 matches!(self.class(), StatusClass::Success)
109 }
110
111 #[must_use]
117 pub const fn is_custom(self) -> bool {
118 !matches!(self.class(), StatusClass::Unknown) && self.0 % 1000 >= 900
119 }
120
121 #[must_use]
123 pub const fn description(self) -> Option<&'static str> {
124 Some(match self.0 {
125 1000 => "Generic success code",
126 2000 => "Generic client error",
127 2001 => "Invalid or missing parameters",
128 2002 => "Not enough information",
129 2003 => "Unknown Location",
130 2004 => "Unknown Token",
131 3000 => "Generic server error",
132 3001 => "Unable to use the client's API",
133 3002 => "Unsupported version",
134 3003 => "No matching endpoints or expected endpoints missing between parties",
135 4000 => "Generic error",
136 4001 => "Unknown receiver (TO address is unknown)",
137 4002 => "Timeout on forwarded request",
138 4003 => "Connection problem (receiving party is not connected)",
139 _ => return None,
140 })
141 }
142}
143
144impl fmt::Display for StatusCode {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 match self.description() {
147 Some(d) => write!(f, "{} ({d})", self.0),
148 None => write!(f, "{}", self.0),
149 }
150 }
151}
152
153impl fmt::Debug for StatusCode {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 write!(f, "StatusCode({self})")
156 }
157}
158
159impl From<u16> for StatusCode {
160 fn from(value: u16) -> Self {
161 Self(value)
162 }
163}
164impl From<StatusCode> for u16 {
165 fn from(value: StatusCode) -> Self {
166 value.0
167 }
168}
169
170#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
174#[non_exhaustive]
175pub enum StatusClass {
176 Success,
178 ClientError,
180 ServerError,
182 HubError,
184 Unknown,
186}
187
188impl StatusClass {
189 #[must_use]
191 pub const fn is_success(self) -> bool {
192 matches!(self, Self::Success)
193 }
194
195 #[must_use]
197 pub const fn is_client_fault(self) -> bool {
198 matches!(self, Self::ClientError)
199 }
200}
201
202impl fmt::Display for StatusClass {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 f.write_str(match self {
205 Self::Success => "success",
206 Self::ClientError => "client error",
207 Self::ServerError => "server error",
208 Self::HubError => "hub error",
209 Self::Unknown => "unknown status class",
210 })
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn classes_follow_the_ranges() {
220 assert_eq!(StatusCode::new(1000).class(), StatusClass::Success);
221 assert_eq!(StatusCode::new(1999).class(), StatusClass::Success);
222 assert_eq!(StatusCode::new(2001).class(), StatusClass::ClientError);
223 assert_eq!(StatusCode::new(3002).class(), StatusClass::ServerError);
224 assert_eq!(StatusCode::new(4003).class(), StatusClass::HubError);
225 assert_eq!(StatusCode::new(5000).class(), StatusClass::Unknown);
226 assert_eq!(StatusCode::new(999).class(), StatusClass::Unknown);
227 }
228
229 #[test]
230 fn custom_ranges_are_recognised() {
231 for code in [1900, 1999, 2900, 3950, 4999] {
232 assert!(StatusCode::new(code).is_custom(), "{code} is in a reserved custom range");
233 }
234 for code in [1000, 2001, 3003, 4000, 2899] {
235 assert!(!StatusCode::new(code).is_custom(), "{code} is a standard code");
236 }
237 assert!(!StatusCode::new(5900).is_custom(), "outside every defined class");
238 }
239
240 #[test]
241 fn serialises_as_a_bare_number() {
242 assert_eq!(serde_json::to_string(&StatusCode::SUCCESS).unwrap(), "1000");
243 let parsed: StatusCode = serde_json::from_str("2001").unwrap();
244 assert_eq!(parsed, StatusCode::INVALID_PARAMETERS);
245 }
246
247 #[test]
248 fn display_includes_the_spec_description() {
249 assert_eq!(StatusCode::INVALID_PARAMETERS.to_string(), "2001 (Invalid or missing parameters)");
250 assert_eq!(StatusCode::new(2901).to_string(), "2901");
251 }
252}