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 fn new(context: C) -> Self {
72 Self { context }
73 }
74
75 #[inline]
77 pub 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 fn layer(&self, inner: S) -> Self::Service {
86 ErrorRailService { inner, context: self.context.clone() }
87 }
88}
89
90#[derive(Clone, Debug)]
95pub struct ErrorRailService<S, C> {
96 inner: S,
97 context: C,
98}
99
100impl<S, C> ErrorRailService<S, C> {
101 #[inline]
103 pub fn new(inner: S, context: C) -> Self {
104 Self { inner, context }
105 }
106
107 #[inline]
109 pub fn inner(&self) -> &S {
110 &self.inner
111 }
112
113 #[inline]
115 pub fn inner_mut(&mut self) -> &mut S {
116 &mut self.inner
117 }
118
119 #[inline]
121 pub fn into_inner(self) -> S {
122 self.inner
123 }
124
125 #[inline]
127 pub fn context(&self) -> &C {
128 &self.context
129 }
130}
131
132impl<S, C, Request> Service<Request> for ErrorRailService<S, C>
133where
134 S: Service<Request>,
135 S::Error: core::fmt::Debug,
136 C: IntoErrorContext + Clone,
137{
138 type Response = S::Response;
139 type Error = ComposableError<S::Error>;
140 type Future = ErrorRailFuture<S::Future, C>;
141
142 #[inline]
143 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
144 self.inner
145 .poll_ready(cx)
146 .map_err(|e| ComposableError::new(e).with_context(self.context.clone()))
147 }
148
149 #[inline]
150 fn call(&mut self, request: Request) -> Self::Future {
151 ErrorRailFuture { inner: self.inner.call(request), context: Some(self.context.clone()) }
152 }
153}
154
155pin_project! {
156 pub struct ErrorRailFuture<F, C> {
160 #[pin]
161 inner: F,
162 context: Option<C>,
163 }
164}
165
166impl<F, T, E, C> Future for ErrorRailFuture<F, C>
167where
168 F: Future<Output = Result<T, E>>,
169 C: IntoErrorContext,
170{
171 type Output = Result<T, ComposableError<E>>;
172
173 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
174 let this = self.project();
175
176 match this.inner.poll(cx) {
177 Poll::Ready(Ok(response)) => Poll::Ready(Ok(response)),
178 Poll::Ready(Err(error)) => {
179 let context = this.context.take().expect("polled after completion");
180 let composable = ComposableError::new(error).with_context(context);
181 Poll::Ready(Err(composable))
182 },
183 Poll::Pending => Poll::Pending,
184 }
185 }
186}
187
188impl<F, T, E, C> FusedFuture for ErrorRailFuture<F, C>
189where
190 F: FusedFuture<Output = Result<T, E>>,
191 C: IntoErrorContext,
192{
193 fn is_terminated(&self) -> bool {
194 self.context.is_none() || self.inner.is_terminated()
195 }
196}
197
198pub trait ServiceErrorExt<Request>: Service<Request> + Sized {
200 fn with_error_context<C>(self, context: C) -> ErrorRailService<Self, C>
210 where
211 C: IntoErrorContext + Clone,
212 {
213 ErrorRailService::new(self, context)
214 }
215}
216
217impl<S, Request> ServiceErrorExt<Request> for S where S: Service<Request> {}