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(mut self, headers: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>) -> Self {
65 for (name, value) in headers {
66 self.headers.push(HeaderEntry {
67 name: name.into(),
68 value: value.into(),
69 });
70 }
71 self
72 }
73
74 #[must_use]
76 pub fn content_type(self, mime_type: impl Into<String>) -> Self {
77 self.header("Content-Type", mime_type)
78 }
79
80 #[must_use]
82 pub fn body(mut self, body: impl Into<String>) -> Self {
83 self.body = Some(body.into().into_bytes());
84 self
85 }
86
87 #[must_use]
89 pub fn body_bytes(mut self, body: impl Into<Vec<u8>>) -> Self {
90 self.body = Some(body.into());
91 self
92 }
93
94 #[must_use]
96 pub fn json<T: serde::Serialize>(self, value: &T) -> Self {
97 let json = serde_json::to_string(value).unwrap_or_default();
98 self.content_type("application/json").body(json)
99 }
100
101 pub async fn path(mut self, path: impl AsRef<Path>) -> Result<Self, NetworkError> {
107 let body = tokio::fs::read(path.as_ref())
108 .await
109 .map_err(|e| NetworkError::IoError(e.to_string()))?;
110 self.body = Some(body);
111
112 if let Some(ext) = path.as_ref().extension().and_then(|e| e.to_str()) {
114 let mime_type = match ext.to_lowercase().as_str() {
115 "html" | "htm" => "text/html",
116 "css" => "text/css",
117 "js" => "application/javascript",
118 "json" => "application/json",
119 "png" => "image/png",
120 "jpg" | "jpeg" => "image/jpeg",
121 "gif" => "image/gif",
122 "svg" => "image/svg+xml",
123 "pdf" => "application/pdf",
124 "txt" => "text/plain",
125 "xml" => "application/xml",
126 _ => "application/octet-stream",
127 };
128 return Ok(self.content_type(mime_type));
129 }
130
131 Ok(self)
132 }
133
134 #[must_use]
138 pub fn response(mut self, response: &FetchedResponse<'_>) -> Self {
139 self.status = response.status;
140 for (name, value) in &response.headers {
141 self.headers.push(HeaderEntry {
142 name: name.clone(),
143 value: value.clone(),
144 });
145 }
146 if let Some(ref body) = response.body {
148 self.body = Some(body.clone());
149 }
150 self
151 }
152
153 pub async fn send(self) -> Result<(), NetworkError> {
159 use base64::Engine;
160
161 let body = self.body.map(|b| {
162 base64::engine::general_purpose::STANDARD.encode(&b)
163 });
164
165 let params = FulfillRequestParams {
166 request_id: self.route.request_id().to_string(),
167 response_code: i32::from(self.status),
168 response_headers: if self.headers.is_empty() {
169 None
170 } else {
171 Some(self.headers)
172 },
173 binary_response_headers: None,
174 body,
175 response_phrase: self.status_text,
176 };
177
178 self.route.send_fulfill(params).await
179 }
180}
181
182#[derive(Debug)]
184pub struct ContinueBuilder<'a> {
185 pub(super) route: &'a Route,
186 pub(super) url: Option<String>,
187 pub(super) method: Option<String>,
188 pub(super) headers: Vec<HeaderEntry>,
189 pub(super) post_data: Option<Vec<u8>>,
190}
191
192impl<'a> ContinueBuilder<'a> {
193 pub(super) fn new(route: &'a Route) -> Self {
194 Self {
195 route,
196 url: None,
197 method: None,
198 headers: Vec::new(),
199 post_data: None,
200 }
201 }
202
203 #[must_use]
205 pub fn url(mut self, url: impl Into<String>) -> Self {
206 self.url = Some(url.into());
207 self
208 }
209
210 #[must_use]
212 pub fn method(mut self, method: impl Into<String>) -> Self {
213 self.method = Some(method.into());
214 self
215 }
216
217 #[must_use]
219 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
220 self.headers.push(HeaderEntry {
221 name: name.into(),
222 value: value.into(),
223 });
224 self
225 }
226
227 #[must_use]
229 pub fn headers(mut self, headers: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>) -> Self {
230 for (name, value) in headers {
231 self.headers.push(HeaderEntry {
232 name: name.into(),
233 value: value.into(),
234 });
235 }
236 self
237 }
238
239 #[must_use]
243 pub fn post_data(mut self, data: impl Into<Vec<u8>>) -> Self {
244 self.post_data = Some(data.into());
245 self
246 }
247
248 pub async fn send(self) -> Result<(), NetworkError> {
254 use base64::Engine;
255
256 let post_data = self.post_data.map(|d| {
257 base64::engine::general_purpose::STANDARD.encode(&d)
258 });
259
260 let params = ContinueRequestParams {
261 request_id: self.route.request_id().to_string(),
262 url: self.url,
263 method: self.method,
264 post_data,
265 headers: if self.headers.is_empty() {
266 None
267 } else {
268 Some(self.headers)
269 },
270 intercept_response: None,
271 };
272
273 self.route.send_continue(params).await
274 }
275}
276
277impl ContinueBuilder<'_> {
279 pub async fn await_continue(self) -> Result<(), NetworkError> {
281 self.send().await
282 }
283}
284
285impl<'a> std::future::IntoFuture for ContinueBuilder<'a> {
286 type Output = Result<(), NetworkError>;
287 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
288
289 fn into_future(self) -> Self::IntoFuture {
290 Box::pin(self.send())
291 }
292}
293
294