bel7_axum/errors/traits.rs
1// Copyright (C) 2025-2026 Michael S. Klishin and Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Core error classification traits.
16//!
17//! These traits provide a common vocabulary for describing error behavior
18//! across different error types in your application.
19
20use std::error::Error;
21
22/// Trait for errors that can be classified as recoverable or not.
23///
24/// Recoverable errors are typically transient failures that may succeed
25/// on retry (timeouts, temporary network issues, etc.).
26///
27/// # Example
28///
29/// ```
30/// use bel7_axum::RecoverableError;
31/// use thiserror::Error;
32///
33/// #[derive(Error, Debug)]
34/// enum MyError {
35/// #[error("connection timeout")]
36/// Timeout,
37/// #[error("invalid input: {0}")]
38/// InvalidInput(String),
39/// }
40///
41/// impl RecoverableError for MyError {
42/// fn is_recoverable(&self) -> bool {
43/// matches!(self, MyError::Timeout)
44/// }
45/// }
46/// ```
47pub trait RecoverableError: Error {
48 /// Returns `true` if this error is potentially recoverable via retry.
49 fn is_recoverable(&self) -> bool;
50}
51
52/// Trait for errors related to network connections.
53///
54/// Useful for connection pool management, reconnection logic, and
55/// distinguishing between connection failures and application errors.
56pub trait ConnectionError: Error {
57 /// Returns `true` if this error indicates the connection was closed.
58 fn is_connection_closed(&self) -> bool;
59
60 /// Returns `true` if this error indicates a connection timeout.
61 fn is_timeout(&self) -> bool {
62 false
63 }
64
65 /// Returns `true` if this error indicates a connection was refused.
66 fn is_connection_refused(&self) -> bool {
67 false
68 }
69}
70
71/// Trait for errors that can provide additional context.
72///
73/// Useful for building rich error messages with suggestions or help text.
74pub trait DiagnosticError: Error {
75 /// Returns suggestions for fixing this error, if any.
76 fn suggestions(&self) -> Vec<String> {
77 Vec::new()
78 }
79
80 /// Returns help text for this error, if any.
81 fn help(&self) -> Option<String> {
82 None
83 }
84
85 /// Returns the position in input where this error occurred, if applicable.
86 fn position(&self) -> Option<usize> {
87 None
88 }
89}
90
91/// Extension trait for checking common error message patterns.
92///
93/// Provides heuristic-based error classification by examining error messages.
94/// Useful when you don't control the error type but need to classify it.
95pub trait ErrorMessageExt {
96 /// Check if the error message suggests a timeout.
97 fn message_suggests_timeout(&self) -> bool;
98
99 /// Check if the error message suggests a closed connection.
100 fn message_suggests_connection_closed(&self) -> bool;
101
102 /// Check if the error message suggests a connection reset.
103 fn message_suggests_connection_reset(&self) -> bool;
104}
105
106impl<E: Error> ErrorMessageExt for E {
107 fn message_suggests_timeout(&self) -> bool {
108 let msg = self.to_string().to_lowercase();
109 msg.contains("timeout") || msg.contains("timed out")
110 }
111
112 fn message_suggests_connection_closed(&self) -> bool {
113 let msg = self.to_string().to_lowercase();
114 msg.contains("closed") || msg.contains("eof") || msg.contains("end of file")
115 }
116
117 fn message_suggests_connection_reset(&self) -> bool {
118 let msg = self.to_string().to_lowercase();
119 msg.contains("reset") || msg.contains("broken pipe") || msg.contains("connection reset")
120 }
121}