1use crate::errors::{ErrorSeverity, UserFriendlyError};
38use std::collections::hash_map::DefaultHasher;
39use std::fmt;
40use std::hash::{Hash, Hasher};
41use thiserror::Error;
42
43#[derive(Debug, Clone)]
45pub enum CodecOperation {
46 Encode,
48 Decode,
50}
51
52impl fmt::Display for CodecOperation {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 CodecOperation::Encode => write!(f, "encode"),
56 CodecOperation::Decode => write!(f, "decode"),
57 }
58 }
59}
60
61#[derive(Debug, Clone)]
63pub enum JwtOperation {
64 Encode,
66 Decode,
68 Validate,
70}
71
72impl fmt::Display for JwtOperation {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 match self {
75 JwtOperation::Encode => write!(f, "encode"),
76 JwtOperation::Decode => write!(f, "decode"),
77 JwtOperation::Validate => write!(f, "validate"),
78 }
79 }
80}
81
82#[derive(Debug, Error)]
84#[non_exhaustive]
85pub enum CodecsError {
86 #[error("Codec error: {operation} - {message}")]
88 Codec {
89 operation: CodecOperation,
91 message: String,
93 payload_type: Option<String>,
95 expected_format: Option<String>,
97 },
98}
99
100impl CodecsError {
101 pub fn codec(operation: CodecOperation, message: impl Into<String>) -> Self {
103 CodecsError::Codec {
104 operation,
105 message: message.into(),
106 payload_type: None,
107 expected_format: None,
108 }
109 }
110
111 pub fn codec_with_format(
113 operation: CodecOperation,
114 message: impl Into<String>,
115 payload_type: Option<String>,
116 expected_format: Option<String>,
117 ) -> Self {
118 CodecsError::Codec {
119 operation,
120 message: message.into(),
121 payload_type,
122 expected_format,
123 }
124 }
125
126 fn support_code_inner(&self) -> String {
127 let mut hasher = DefaultHasher::new();
128 match self {
129 CodecsError::Codec {
130 operation,
131 payload_type,
132 ..
133 } => {
134 format!("CODEC-{}-{:X}", operation.to_string().to_uppercase(), {
135 format!("{:?}{:?}", operation, payload_type).hash(&mut hasher);
136 hasher.finish() % 10000
137 })
138 }
139 }
140 }
141}
142
143impl UserFriendlyError for CodecsError {
144 fn user_message(&self) -> String {
145 match self {
146 CodecsError::Codec { operation, .. } => match operation {
147 CodecOperation::Encode => {
148 "We couldn't process your data in the required format. Please check your input and try again.".to_string()
149 }
150 CodecOperation::Decode => {
151 "We received data in an unexpected format. This might be a temporary issue - please try again.".to_string()
152 }
153 },
154 }
155 }
156
157 fn developer_message(&self) -> String {
158 match self {
159 CodecsError::Codec {
160 operation,
161 message,
162 payload_type,
163 expected_format,
164 } => {
165 let payload_context = payload_type
166 .as_ref()
167 .map(|pt| format!(" [Payload: {}]", pt))
168 .unwrap_or_default();
169 let format_context = expected_format
170 .as_ref()
171 .map(|ef| format!(" [Expected: {}]", ef))
172 .unwrap_or_default();
173 format!(
174 "Codec contract violation during {} operation: {}{}{}",
175 operation, message, payload_context, format_context
176 )
177 }
178 }
179 }
180
181 fn support_code(&self) -> String {
182 self.support_code_inner()
183 }
184
185 fn severity(&self) -> ErrorSeverity {
186 match self {
187 CodecsError::Codec { operation, .. } => match operation {
188 CodecOperation::Encode | CodecOperation::Decode => ErrorSeverity::Error,
189 },
190 }
191 }
192
193 fn suggested_actions(&self) -> Vec<String> {
194 match self {
195 CodecsError::Codec { operation, .. } => match operation {
196 CodecOperation::Encode => vec![
197 "Check that all required fields are filled out correctly".to_string(),
198 "Ensure special characters are properly formatted".to_string(),
199 "Try simplifying your input and gradually add complexity".to_string(),
200 "Contact support if data formatting requirements are unclear".to_string(),
201 ],
202 CodecOperation::Decode => vec![
203 "This is likely a temporary system issue".to_string(),
204 "Try refreshing the page and repeating your action".to_string(),
205 "Clear your browser cache if the problem persists".to_string(),
206 "Contact support if you continue receiving malformed data".to_string(),
207 ],
208 },
209 }
210 }
211
212 fn is_retryable(&self) -> bool {
213 match self {
214 CodecsError::Codec { operation, .. } => match operation {
215 CodecOperation::Encode => true, CodecOperation::Decode => true, },
218 }
219 }
220}
221
222#[derive(Debug, Error)]
224#[non_exhaustive]
225pub enum JwtError {
226 #[error("JWT error: {operation} - {message}")]
228 Processing {
229 operation: JwtOperation,
231 message: String,
233 token_preview: Option<String>,
235 },
236}
237
238impl JwtError {
239 pub fn processing(operation: JwtOperation, message: impl Into<String>) -> Self {
241 JwtError::Processing {
242 operation,
243 message: message.into(),
244 token_preview: None,
245 }
246 }
247
248 pub fn processing_with_preview(
250 operation: JwtOperation,
251 message: impl Into<String>,
252 token_preview: Option<String>,
253 ) -> Self {
254 JwtError::Processing {
255 operation,
256 message: message.into(),
257 token_preview,
258 }
259 }
260
261 fn support_code_inner(&self) -> String {
262 match self {
263 JwtError::Processing { operation, .. } => {
264 format!("JWT-{}", operation.to_string().to_uppercase())
265 }
266 }
267 }
268}
269
270impl UserFriendlyError for JwtError {
271 fn user_message(&self) -> String {
272 match self {
273 JwtError::Processing { operation, .. } => match operation {
274 JwtOperation::Encode => {
275 "We're having trouble with the authentication system. Please try signing in again.".to_string()
276 }
277 JwtOperation::Decode | JwtOperation::Validate => {
278 "Your session appears to be invalid. Please sign in again to continue.".to_string()
279 }
280 },
281 }
282 }
283
284 fn developer_message(&self) -> String {
285 match self {
286 JwtError::Processing {
287 operation,
288 message,
289 token_preview,
290 } => {
291 let token_context = token_preview
292 .as_ref()
293 .map(|t| format!(" [Token Preview: {}]", t))
294 .unwrap_or_default();
295 format!(
296 "JWT {} operation failed: {}{}",
297 operation, message, token_context
298 )
299 }
300 }
301 }
302
303 fn support_code(&self) -> String {
304 self.support_code_inner()
305 }
306
307 fn severity(&self) -> ErrorSeverity {
308 match self {
309 JwtError::Processing { operation, .. } => match operation {
310 JwtOperation::Encode => ErrorSeverity::Error,
311 _ => ErrorSeverity::Warning,
312 },
313 }
314 }
315
316 fn suggested_actions(&self) -> Vec<String> {
317 match self {
318 JwtError::Processing { operation, .. } => match operation {
319 JwtOperation::Encode => vec![
320 "Try signing in again".to_string(),
321 "Clear your browser cookies and try again".to_string(),
322 "Contact support if you cannot sign in after multiple attempts".to_string(),
323 ],
324 JwtOperation::Decode | JwtOperation::Validate => vec![
325 "Sign out completely and sign back in".to_string(),
326 "Clear your browser cache and cookies".to_string(),
327 "Try using a different browser or incognito mode".to_string(),
328 ],
329 },
330 }
331 }
332
333 fn is_retryable(&self) -> bool {
334 match self {
335 JwtError::Processing { operation, .. } => match operation {
336 JwtOperation::Encode => true, _ => true,
338 },
339 }
340 }
341}