tower_resilience_bulkhead/
error.rs1use tower_resilience_core::ResilienceError;
4
5#[derive(Debug, Clone, thiserror::Error)]
7pub enum BulkheadError {
8 #[error("bulkhead is full: max concurrent calls ({max_concurrent_calls}) reached")]
10 BulkheadFull {
11 max_concurrent_calls: usize,
13 },
14 #[error("timeout waiting for bulkhead permit")]
16 Timeout,
17}
18
19pub type Result<T> = std::result::Result<T, BulkheadError>;
21
22#[derive(Debug)]
46pub enum BulkheadServiceError<E> {
47 Bulkhead(BulkheadError),
49 Inner(E),
51}
52
53impl<E> BulkheadServiceError<E> {
54 pub fn is_bulkhead(&self) -> bool {
56 matches!(self, BulkheadServiceError::Bulkhead(_))
57 }
58
59 pub fn is_inner(&self) -> bool {
61 matches!(self, BulkheadServiceError::Inner(_))
62 }
63
64 pub fn into_inner(self) -> Option<E> {
66 match self {
67 BulkheadServiceError::Bulkhead(_) => None,
68 BulkheadServiceError::Inner(e) => Some(e),
69 }
70 }
71
72 pub fn bulkhead_error(&self) -> Option<&BulkheadError> {
74 match self {
75 BulkheadServiceError::Bulkhead(e) => Some(e),
76 BulkheadServiceError::Inner(_) => None,
77 }
78 }
79}
80
81impl<E: std::fmt::Display> std::fmt::Display for BulkheadServiceError<E> {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 BulkheadServiceError::Bulkhead(e) => write!(f, "{}", e),
85 BulkheadServiceError::Inner(e) => write!(f, "inner service error: {}", e),
86 }
87 }
88}
89
90impl<E: std::error::Error + 'static> std::error::Error for BulkheadServiceError<E> {
91 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
92 match self {
93 BulkheadServiceError::Bulkhead(e) => Some(e),
94 BulkheadServiceError::Inner(e) => Some(e),
95 }
96 }
97}
98
99impl<E> From<BulkheadError> for BulkheadServiceError<E> {
100 fn from(err: BulkheadError) -> Self {
101 BulkheadServiceError::Bulkhead(err)
102 }
103}
104
105impl<E> From<BulkheadError> for ResilienceError<E> {
107 fn from(err: BulkheadError) -> Self {
108 match err {
109 BulkheadError::Timeout => ResilienceError::Timeout { layer: "bulkhead" },
110 BulkheadError::BulkheadFull {
111 max_concurrent_calls,
112 } => ResilienceError::BulkheadFull {
113 concurrent_calls: max_concurrent_calls,
114 max_concurrent: max_concurrent_calls,
115 },
116 }
117 }
118}
119
120impl<E> From<BulkheadServiceError<E>> for ResilienceError<E> {
121 fn from(err: BulkheadServiceError<E>) -> Self {
122 match err {
123 BulkheadServiceError::Bulkhead(e) => e.into(),
124 BulkheadServiceError::Inner(e) => ResilienceError::Application(e),
125 }
126 }
127}
128
129impl<E> From<BulkheadServiceError<ResilienceError<E>>> for ResilienceError<E> {
133 fn from(err: BulkheadServiceError<ResilienceError<E>>) -> Self {
134 match err {
135 BulkheadServiceError::Bulkhead(e) => e.into(),
136 BulkheadServiceError::Inner(re) => re,
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 const _: () = {
148 const fn assert_send_sync_static<T: Send + Sync + 'static>() {}
149 assert_send_sync_static::<BulkheadError>();
150 };
151
152 #[test]
153 fn test_into_box_error() {
154 let err = BulkheadError::BulkheadFull {
155 max_concurrent_calls: 10,
156 };
157 let boxed: Box<dyn std::error::Error + Send + Sync> = Box::new(err);
158 assert!(boxed.to_string().contains("bulkhead is full"));
159 }
160
161 #[test]
162 fn test_timeout_into_box_error() {
163 let err = BulkheadError::Timeout;
164 let boxed: Box<dyn std::error::Error + Send + Sync> = Box::new(err);
165 assert!(boxed.to_string().contains("timeout"));
166 }
167}