1use std::future::Future;
9use std::pin::Pin;
10use std::sync::Arc;
11
12use bytes::Bytes;
13use http::{HeaderMap, Method};
14use url::Url;
15
16use crate::error::Error;
17use crate::response::Response;
18use crate::Result;
19
20#[derive(Debug, Clone)]
22pub struct RequestContext {
23 pub url: Url,
25 pub method: Method,
27 pub headers: HeaderMap,
29 pub body: Option<Bytes>,
31 pub retry_attempt: u32,
35}
36
37#[derive(Debug, Clone)]
39pub struct ResponseContext {
40 pub request: RequestContext,
42 pub response: Response,
44}
45
46#[derive(Debug, Clone)]
48pub struct SuccessContext {
49 pub request: RequestContext,
51 pub response: Response,
53}
54
55#[derive(Debug, Clone)]
57pub struct ErrorContext {
58 pub request: RequestContext,
60 pub response: Option<Response>,
62 pub error: Error,
64}
65
66type RequestHookFn = Arc<
67 dyn Fn(RequestContext) -> Pin<Box<dyn Future<Output = Result<RequestContext>> + Send>>
68 + Send
69 + Sync,
70>;
71
72type ResponseHookFn = Arc<
73 dyn Fn(ResponseContext) -> Pin<Box<dyn Future<Output = Result<Response>> + Send>> + Send + Sync,
74>;
75
76type SuccessHookFn =
77 Arc<dyn Fn(SuccessContext) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
78
79type ErrorHookFn =
80 Arc<dyn Fn(ErrorContext) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
81
82type RetryHookFn =
83 Arc<dyn Fn(ResponseContext) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
84
85#[derive(Clone, Default)]
87pub struct Hooks {
88 pub(crate) on_request: Vec<RequestHookFn>,
89 pub(crate) on_response: Vec<ResponseHookFn>,
90 pub(crate) on_success: Vec<SuccessHookFn>,
91 pub(crate) on_error: Vec<ErrorHookFn>,
92 pub(crate) on_retry: Vec<RetryHookFn>,
93}
94
95impl Hooks {
96 pub fn new() -> Self {
98 Self::default()
99 }
100
101 pub fn on_request<F, Fut>(mut self, f: F) -> Self
122 where
123 F: Fn(RequestContext) -> Fut + Send + Sync + 'static,
124 Fut: Future<Output = Result<RequestContext>> + Send + 'static,
125 {
126 self.on_request.push(Arc::new(move |ctx| Box::pin(f(ctx))));
127 self
128 }
129
130 pub fn on_response<F, Fut>(mut self, f: F) -> Self
132 where
133 F: Fn(ResponseContext) -> Fut + Send + Sync + 'static,
134 Fut: Future<Output = Result<Response>> + Send + 'static,
135 {
136 self.on_response.push(Arc::new(move |ctx| Box::pin(f(ctx))));
137 self
138 }
139
140 pub fn on_success<F, Fut>(mut self, f: F) -> Self
142 where
143 F: Fn(SuccessContext) -> Fut + Send + Sync + 'static,
144 Fut: Future<Output = ()> + Send + 'static,
145 {
146 self.on_success.push(Arc::new(move |ctx| Box::pin(f(ctx))));
147 self
148 }
149
150 pub fn on_error<F, Fut>(mut self, f: F) -> Self
152 where
153 F: Fn(ErrorContext) -> Fut + Send + Sync + 'static,
154 Fut: Future<Output = ()> + Send + 'static,
155 {
156 self.on_error.push(Arc::new(move |ctx| Box::pin(f(ctx))));
157 self
158 }
159
160 pub fn on_retry<F, Fut>(mut self, f: F) -> Self
162 where
163 F: Fn(ResponseContext) -> Fut + Send + Sync + 'static,
164 Fut: Future<Output = ()> + Send + 'static,
165 {
166 self.on_retry.push(Arc::new(move |ctx| Box::pin(f(ctx))));
167 self
168 }
169
170 pub(crate) fn merge(mut self, other: Hooks) -> Self {
171 self.on_request.extend(other.on_request);
172 self.on_response.extend(other.on_response);
173 self.on_success.extend(other.on_success);
174 self.on_error.extend(other.on_error);
175 self.on_retry.extend(other.on_retry);
176 self
177 }
178
179 pub(crate) async fn run_on_request(&self, mut ctx: RequestContext) -> Result<RequestContext> {
180 for hook in &self.on_request {
181 ctx = hook(ctx).await?;
182 }
183 Ok(ctx)
184 }
185
186 pub(crate) async fn run_on_response(&self, ctx: ResponseContext) -> Result<Response> {
187 let request = ctx.request;
188 let mut response = ctx.response;
189 for hook in &self.on_response {
190 response = hook(ResponseContext {
191 request: request.clone(),
192 response,
193 })
194 .await?;
195 }
196 Ok(response)
197 }
198
199 pub(crate) async fn run_on_success(&self, ctx: SuccessContext) {
200 for hook in &self.on_success {
201 hook(ctx.clone()).await;
202 }
203 }
204
205 pub(crate) async fn run_on_error(&self, ctx: ErrorContext) {
206 for hook in &self.on_error {
207 hook(ctx.clone()).await;
208 }
209 }
210
211 pub(crate) async fn run_on_retry(&self, ctx: ResponseContext) {
212 for hook in &self.on_retry {
213 hook(ctx.clone()).await;
214 }
215 }
216}