1use core::future::Future;
27use core::pin::Pin;
28use core::task::{Context, Poll};
29
30use futures_core::future::FusedFuture;
31use pin_project_lite::pin_project;
32use tower::{Layer, Service};
33
34use crate::traits::IntoErrorContext;
35use crate::types::ComposableError;
36
37#[derive(Clone, Debug)]
62pub struct ErrorRailLayer<C> {
63 context: C,
64}
65
66impl<C> ErrorRailLayer<C> {
67 #[inline]
71 pub const fn new(context: C) -> Self {
72 Self { context }
73 }
74
75 #[inline]
77 pub const fn context(&self) -> &C {
78 &self.context
79 }
80}
81
82impl<S, C: Clone> Layer<S> for ErrorRailLayer<C> {
83 type Service = ErrorRailService<S, C>;
84
85 #[inline]
86 fn layer(&self, inner: S) -> Self::Service {
87 ErrorRailService { inner, context: self.context.clone() }
88 }
89}
90
91#[derive(Clone, Debug)]
96pub struct ErrorRailService<S, C> {
97 inner: S,
98 context: C,
99}
100
101impl<S, C> ErrorRailService<S, C> {
102 #[inline]
104 pub const fn new(inner: S, context: C) -> Self {
105 Self { inner, context }
106 }
107
108 #[inline]
110 pub const fn inner(&self) -> &S {
111 &self.inner
112 }
113
114 #[inline]
116 pub fn inner_mut(&mut self) -> &mut S {
117 &mut self.inner
118 }
119
120 #[inline]
122 pub fn into_inner(self) -> S {
123 self.inner
124 }
125
126 #[inline]
128 pub const fn context(&self) -> &C {
129 &self.context
130 }
131}
132
133impl<S, C, Request> Service<Request> for ErrorRailService<S, C>
134where
135 S: Service<Request>,
136 S::Error: core::fmt::Debug,
137 C: IntoErrorContext + Clone,
138{
139 type Response = S::Response;
140 type Error = ComposableError<S::Error>;
141 type Future = ErrorRailFuture<S::Future, C>;
142
143 #[inline]
144 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
145 self.inner
146 .poll_ready(cx)
147 .map_err(|e| ComposableError::new(e).with_context(self.context.clone()))
148 }
149
150 #[inline]
151 fn call(&mut self, request: Request) -> Self::Future {
152 ErrorRailFuture::new(self.inner.call(request), self.context.clone())
153 }
154}
155
156pin_project! {
157 #[must_use = "futures do nothing unless polled"]
161 pub struct ErrorRailFuture<F, C> {
162 #[pin]
163 inner: F,
164 context: Option<C>,
165 }
166}
167
168impl<F, C> ErrorRailFuture<F, C> {
169 #[inline]
171 fn new(inner: F, context: C) -> Self {
172 Self { inner, context: Some(context) }
173 }
174}
175
176impl<F, T, E, C> Future for ErrorRailFuture<F, C>
177where
178 F: Future<Output = Result<T, E>>,
179 C: IntoErrorContext,
180{
181 type Output = Result<T, ComposableError<E>>;
182
183 #[inline]
184 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
185 let this = self.project();
186
187 match this.inner.poll(cx) {
188 Poll::Ready(Ok(response)) => Poll::Ready(Ok(response)),
189 Poll::Ready(Err(error)) => {
190 let context = this.context.take().expect("polled after completion");
192 Poll::Ready(Err(ComposableError::new(error).with_context(context)))
193 },
194 Poll::Pending => Poll::Pending,
195 }
196 }
197}
198
199impl<F, T, E, C> FusedFuture for ErrorRailFuture<F, C>
200where
201 F: FusedFuture<Output = Result<T, E>>,
202 C: IntoErrorContext,
203{
204 #[inline]
205 fn is_terminated(&self) -> bool {
206 self.context.is_none() || self.inner.is_terminated()
207 }
208}
209
210pub trait ServiceErrorExt<Request>: Service<Request> + Sized {
212 fn with_error_context<C>(self, context: C) -> ErrorRailService<Self, C>
222 where
223 C: IntoErrorContext + Clone,
224 {
225 ErrorRailService::new(self, context)
226 }
227}
228
229impl<S, Request> ServiceErrorExt<Request> for S where S: Service<Request> {}