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,
24 pub method: Method,
25 pub headers: HeaderMap,
26 pub body: Option<Bytes>,
27 pub retry_attempt: u32,
31}
32
33#[derive(Debug, Clone)]
35pub struct ResponseContext {
36 pub request: RequestContext,
37 pub response: Response,
38}
39
40#[derive(Debug, Clone)]
42pub struct SuccessContext {
43 pub request: RequestContext,
44 pub response: Response,
45}
46
47#[derive(Debug, Clone)]
49pub struct ErrorContext {
50 pub request: RequestContext,
51 pub response: Option<Response>,
52 pub error: Error,
53}
54
55type RequestHookFn = Arc<
56 dyn Fn(RequestContext) -> Pin<Box<dyn Future<Output = Result<RequestContext>> + Send>>
57 + Send
58 + Sync,
59>;
60
61type ResponseHookFn = Arc<
62 dyn Fn(ResponseContext) -> Pin<Box<dyn Future<Output = Result<Response>> + Send>> + Send + Sync,
63>;
64
65type SuccessHookFn =
66 Arc<dyn Fn(SuccessContext) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
67
68type ErrorHookFn =
69 Arc<dyn Fn(ErrorContext) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
70
71type RetryHookFn =
72 Arc<dyn Fn(ResponseContext) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
73
74#[derive(Clone, Default)]
76pub struct Hooks {
77 pub(crate) on_request: Vec<RequestHookFn>,
78 pub(crate) on_response: Vec<ResponseHookFn>,
79 pub(crate) on_success: Vec<SuccessHookFn>,
80 pub(crate) on_error: Vec<ErrorHookFn>,
81 pub(crate) on_retry: Vec<RetryHookFn>,
82}
83
84impl Hooks {
85 pub fn new() -> Self {
86 Self::default()
87 }
88
89 pub fn on_request<F, Fut>(mut self, f: F) -> Self
91 where
92 F: Fn(RequestContext) -> Fut + Send + Sync + 'static,
93 Fut: Future<Output = Result<RequestContext>> + Send + 'static,
94 {
95 self.on_request.push(Arc::new(move |ctx| Box::pin(f(ctx))));
96 self
97 }
98
99 pub fn on_response<F, Fut>(mut self, f: F) -> Self
101 where
102 F: Fn(ResponseContext) -> Fut + Send + Sync + 'static,
103 Fut: Future<Output = Result<Response>> + Send + 'static,
104 {
105 self.on_response.push(Arc::new(move |ctx| Box::pin(f(ctx))));
106 self
107 }
108
109 pub fn on_success<F, Fut>(mut self, f: F) -> Self
110 where
111 F: Fn(SuccessContext) -> Fut + Send + Sync + 'static,
112 Fut: Future<Output = ()> + Send + 'static,
113 {
114 self.on_success.push(Arc::new(move |ctx| Box::pin(f(ctx))));
115 self
116 }
117
118 pub fn on_error<F, Fut>(mut self, f: F) -> Self
119 where
120 F: Fn(ErrorContext) -> Fut + Send + Sync + 'static,
121 Fut: Future<Output = ()> + Send + 'static,
122 {
123 self.on_error.push(Arc::new(move |ctx| Box::pin(f(ctx))));
124 self
125 }
126
127 pub fn on_retry<F, Fut>(mut self, f: F) -> Self
128 where
129 F: Fn(ResponseContext) -> Fut + Send + Sync + 'static,
130 Fut: Future<Output = ()> + Send + 'static,
131 {
132 self.on_retry.push(Arc::new(move |ctx| Box::pin(f(ctx))));
133 self
134 }
135
136 pub(crate) fn merge(mut self, other: Hooks) -> Self {
137 self.on_request.extend(other.on_request);
138 self.on_response.extend(other.on_response);
139 self.on_success.extend(other.on_success);
140 self.on_error.extend(other.on_error);
141 self.on_retry.extend(other.on_retry);
142 self
143 }
144
145 pub(crate) async fn run_on_request(&self, mut ctx: RequestContext) -> Result<RequestContext> {
146 for hook in &self.on_request {
147 ctx = hook(ctx).await?;
148 }
149 Ok(ctx)
150 }
151
152 pub(crate) async fn run_on_response(&self, ctx: ResponseContext) -> Result<Response> {
153 let request = ctx.request;
154 let mut response = ctx.response;
155 for hook in &self.on_response {
156 response = hook(ResponseContext {
157 request: request.clone(),
158 response,
159 })
160 .await?;
161 }
162 Ok(response)
163 }
164
165 pub(crate) async fn run_on_success(&self, ctx: SuccessContext) {
166 for hook in &self.on_success {
167 hook(ctx.clone()).await;
168 }
169 }
170
171 pub(crate) async fn run_on_error(&self, ctx: ErrorContext) {
172 for hook in &self.on_error {
173 hook(ctx.clone()).await;
174 }
175 }
176
177 pub(crate) async fn run_on_retry(&self, ctx: ResponseContext) {
178 for hook in &self.on_retry {
179 hook(ctx.clone()).await;
180 }
181 }
182}