codetether_browser/browser/offline/
record.rs1use anyhow::Result;
5use base64::Engine as _;
6use base64::engine::general_purpose::STANDARD;
7use reqwest::blocking::Client;
8use std::collections::BTreeMap;
9use std::path::Path;
10
11#[derive(Debug, serde::Serialize, serde::Deserialize)]
12pub struct Capture {
13 pub url: String,
14 pub status: u16,
15 pub headers: BTreeMap<String, String>,
16 pub content_type: Option<String>,
17 pub body_base64: String,
19}
20
21pub fn run(url: &str, out: &Path) -> Result<String> {
22 let resp = Client::builder().build()?.get(url).send()?;
23 let status = resp.status().as_u16();
24 let headers: BTreeMap<String, String> = resp
25 .headers()
26 .iter()
27 .map(|(k, v)| {
28 (
29 k.as_str().to_ascii_lowercase(),
30 String::from_utf8_lossy(v.as_bytes()).to_string(),
31 )
32 })
33 .collect();
34 let content_type = headers.get("content-type").cloned();
35 let bytes = resp.bytes()?;
36 let capture = Capture {
37 url: url.into(),
38 status,
39 headers,
40 content_type,
41 body_base64: STANDARD.encode(&bytes),
42 };
43 std::fs::write(out, serde_json::to_string_pretty(&capture)?)?;
44 Ok(serde_json::to_string_pretty(&serde_json::json!({
45 "saved": out.display().to_string(),
46 "status": capture.status,
47 "bytes": bytes.len(),
48 "content_type": capture.content_type,
49 }))?)
50}