viewpoint_core/network/route_builders/
mod.rs1use std::future::Future;
8use std::path::Path;
9use std::pin::Pin;
10
11use viewpoint_cdp::protocol::fetch::{ContinueRequestParams, FulfillRequestParams, HeaderEntry};
12
13use super::route::Route;
14use super::route_fetch::FetchedResponse;
15use crate::error::NetworkError;
16
17#[derive(Debug)]
19pub struct FulfillBuilder<'a> {
20 pub(super) route: &'a Route,
21 pub(super) status: u16,
22 pub(super) status_text: Option<String>,
23 pub(super) headers: Vec<HeaderEntry>,
24 pub(super) body: Option<Vec<u8>>,
25}
26
27impl<'a> FulfillBuilder<'a> {
28 pub(super) fn new(route: &'a Route) -> Self {
29 Self {
30 route,
31 status: 200,
32 status_text: None,
33 headers: Vec::new(),
34 body: None,
35 }
36 }
37
38 #[must_use]
40 pub fn status(mut self, code: u16) -> Self {
41 self.status = code;
42 self
43 }
44
45 #[must_use]
47 pub fn status_text(mut self, text: impl Into<String>) -> Self {
48 self.status_text = Some(text.into());
49 self
50 }
51
52 #[must_use]
54 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
55 self.headers.push(HeaderEntry {
56 name: name.into(),
57 value: value.into(),
58 });
59 self
60 }
61
62 #[must_use]
64 pub fn headers(
65 mut self,
66 headers: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
67 ) -> Self {
68 for (name, value) in headers {
69 self.headers.push(HeaderEntry {
70 name: name.into(),
71 value: value.into(),
72 });
73 }
74 self
75 }
76
77 #[must_use]
79 pub fn content_type(self, mime_type: impl Into<String>) -> Self {
80 self.header("Content-Type", mime_type)
81 }
82
83 #[must_use]
85 pub fn body(mut self, body: impl Into<String>) -> Self {
86 self.body = Some(body.into().into_bytes());
87 self
88 }
89
90 #[must_use]
92 pub fn body_bytes(mut self, body: impl Into<Vec<u8>>) -> Self {
93 self.body = Some(body.into());
94 self
95 }
96
97 #[must_use]
99 pub fn json<T: serde::Serialize>(self, value: &T) -> Self {
100 let json = serde_json::to_string(value).unwrap_or_default();
101 self.content_type("application/json").body(json)
102 }
103
104 pub async fn path(mut self, path: impl AsRef<Path>) -> Result<Self, NetworkError> {
110 let body = tokio::fs::read(path.as_ref())
111 .await
112 .map_err(|e| NetworkError::IoError(e.to_string()))?;
113 self.body = Some(body);
114
115 if let Some(ext) = path.as_ref().extension().and_then(|e| e.to_str()) {
117 let mime_type = match ext.to_lowercase().as_str() {
118 "html" | "htm" => "text/html",
119 "css" => "text/css",
120 "js" => "application/javascript",
121 "json" => "application/json",
122 "png" => "image/png",
123 "jpg" | "jpeg" => "image/jpeg",
124 "gif" => "image/gif",
125 "svg" => "image/svg+xml",
126 "pdf" => "application/pdf",
127 "txt" => "text/plain",
128 "xml" => "application/xml",
129 _ => "application/octet-stream",
130 };
131 return Ok(self.content_type(mime_type));
132 }
133
134 Ok(self)
135 }
136
137 #[must_use]
141 pub fn response(mut self, response: &FetchedResponse<'_>) -> Self {
142 self.status = response.status;
143 for (name, value) in &response.headers {
144 self.headers.push(HeaderEntry {
145 name: name.clone(),
146 value: value.clone(),
147 });
148 }
149 if let Some(ref body) = response.body {
151 self.body = Some(body.clone());
152 }
153 self
154 }
155
156 pub async fn send(self) -> Result<(), NetworkError> {
162 use base64::Engine;
163
164 let body = self
165 .body
166 .map(|b| base64::engine::general_purpose::STANDARD.encode(&b));
167
168 let params = FulfillRequestParams {
169 request_id: self.route.request_id().to_string(),
170 response_code: i32::from(self.status),
171 response_headers: if self.headers.is_empty() {
172 None
173 } else {
174 Some(self.headers)
175 },
176 binary_response_headers: None,
177 body,
178 response_phrase: self.status_text,
179 };
180
181 self.route.send_fulfill(params).await
182 }
183}
184
185#[derive(Debug)]
187pub struct ContinueBuilder<'a> {
188 pub(super) route: &'a Route,
189 pub(super) url: Option<String>,
190 pub(super) method: Option<String>,
191 pub(super) headers: Vec<HeaderEntry>,
192 pub(super) post_data: Option<Vec<u8>>,
193}
194
195impl<'a> ContinueBuilder<'a> {
196 pub(super) fn new(route: &'a Route) -> Self {
197 Self {
198 route,
199 url: None,
200 method: None,
201 headers: Vec::new(),
202 post_data: None,
203 }
204 }
205
206 #[must_use]
208 pub fn url(mut self, url: impl Into<String>) -> Self {
209 self.url = Some(url.into());
210 self
211 }
212
213 #[must_use]
215 pub fn method(mut self, method: impl Into<String>) -> Self {
216 self.method = Some(method.into());
217 self
218 }
219
220 #[must_use]
222 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
223 self.headers.push(HeaderEntry {
224 name: name.into(),
225 value: value.into(),
226 });
227 self
228 }
229
230 #[must_use]
232 pub fn headers(
233 mut self,
234 headers: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
235 ) -> Self {
236 for (name, value) in headers {
237 self.headers.push(HeaderEntry {
238 name: name.into(),
239 value: value.into(),
240 });
241 }
242 self
243 }
244
245 #[must_use]
249 pub fn post_data(mut self, data: impl Into<Vec<u8>>) -> Self {
250 self.post_data = Some(data.into());
251 self
252 }
253
254 pub async fn send(self) -> Result<(), NetworkError> {
260 use base64::Engine;
261
262 let post_data = self
263 .post_data
264 .map(|d| base64::engine::general_purpose::STANDARD.encode(&d));
265
266 let params = ContinueRequestParams {
267 request_id: self.route.request_id().to_string(),
268 url: self.url,
269 method: self.method,
270 post_data,
271 headers: if self.headers.is_empty() {
272 None
273 } else {
274 Some(self.headers)
275 },
276 intercept_response: None,
277 };
278
279 self.route.send_continue(params).await
280 }
281}
282
283impl ContinueBuilder<'_> {
285 pub async fn await_continue(self) -> Result<(), NetworkError> {
287 self.send().await
288 }
289}
290
291impl<'a> std::future::IntoFuture for ContinueBuilder<'a> {
292 type Output = Result<(), NetworkError>;
293 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
294
295 fn into_future(self) -> Self::IntoFuture {
296 Box::pin(self.send())
297 }
298}
299
300