1use core::future::Future;
27use core::pin::Pin;
28use core::task::{Context, Poll};
29
30use pin_project_lite::pin_project;
31use tower::{Layer, Service};
32
33use crate::traits::IntoErrorContext;
34use crate::types::ComposableError;
35
36#[derive(Clone, Debug)]
61pub struct ErrorRailLayer<C> {
62 context: C,
63}
64
65impl<C> ErrorRailLayer<C> {
66 #[inline]
70 pub fn new(context: C) -> Self {
71 Self { context }
72 }
73
74 #[inline]
76 pub fn context(&self) -> &C {
77 &self.context
78 }
79}
80
81impl<S, C: Clone> Layer<S> for ErrorRailLayer<C> {
82 type Service = ErrorRailService<S, C>;
83
84 fn layer(&self, inner: S) -> Self::Service {
85 ErrorRailService { inner, context: self.context.clone() }
86 }
87}
88
89#[derive(Clone, Debug)]
94pub struct ErrorRailService<S, C> {
95 inner: S,
96 context: C,
97}
98
99impl<S, C> ErrorRailService<S, C> {
100 #[inline]
102 pub fn new(inner: S, context: C) -> Self {
103 Self { inner, context }
104 }
105
106 #[inline]
108 pub fn inner(&self) -> &S {
109 &self.inner
110 }
111
112 #[inline]
114 pub fn inner_mut(&mut self) -> &mut S {
115 &mut self.inner
116 }
117
118 #[inline]
120 pub fn into_inner(self) -> S {
121 self.inner
122 }
123
124 #[inline]
126 pub fn context(&self) -> &C {
127 &self.context
128 }
129}
130
131impl<S, C, Request> Service<Request> for ErrorRailService<S, C>
132where
133 S: Service<Request>,
134 S::Error: core::fmt::Debug,
135 C: IntoErrorContext + Clone,
136{
137 type Response = S::Response;
138 type Error = ComposableError<S::Error>;
139 type Future = ErrorRailFuture<S::Future, C>;
140
141 #[inline]
142 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
143 self.inner.poll_ready(cx).map_err(ComposableError::new)
144 }
145
146 #[inline]
147 fn call(&mut self, request: Request) -> Self::Future {
148 ErrorRailFuture { inner: self.inner.call(request), context: Some(self.context.clone()) }
149 }
150}
151
152pin_project! {
153 pub struct ErrorRailFuture<F, C> {
157 #[pin]
158 inner: F,
159 context: Option<C>,
160 }
161}
162
163impl<F, T, E, C> Future for ErrorRailFuture<F, C>
164where
165 F: Future<Output = Result<T, E>>,
166 C: IntoErrorContext,
167{
168 type Output = Result<T, ComposableError<E>>;
169
170 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
171 let this = self.project();
172
173 match this.inner.poll(cx) {
174 Poll::Ready(Ok(response)) => Poll::Ready(Ok(response)),
175 Poll::Ready(Err(error)) => {
176 let context = this.context.take().expect("polled after completion");
177 let composable = ComposableError::new(error).with_context(context);
178 Poll::Ready(Err(composable))
179 },
180 Poll::Pending => Poll::Pending,
181 }
182 }
183}
184
185pub trait ServiceErrorExt<Request>: Service<Request> + Sized {
187 fn with_error_context<C>(self, context: C) -> ErrorRailService<Self, C>
197 where
198 C: IntoErrorContext + Clone,
199 {
200 ErrorRailService::new(self, context)
201 }
202}
203
204impl<S, Request> ServiceErrorExt<Request> for S where S: Service<Request> {}