halldyll_core/fetch/
response.rs1use bytes::Bytes;
4use reqwest::header::HeaderMap;
5use reqwest::StatusCode;
6use std::collections::HashMap;
7use url::Url;
8
9use crate::types::provenance::{RedirectHop, RequestTimings};
10
11#[derive(Debug)]
13pub struct FetchResponse {
14 pub final_url: Url,
16 pub status: StatusCode,
18 pub headers: HeaderMap,
20 pub body: Bytes,
22 pub redirect_chain: Vec<RedirectHop>,
24 pub timings: RequestTimings,
26 pub compressed_size: Option<u64>,
28 pub not_modified: bool,
30}
31
32impl FetchResponse {
33 pub fn new(final_url: Url, status: StatusCode, headers: HeaderMap, body: Bytes) -> Self {
35 Self {
36 final_url,
37 status,
38 headers,
39 body,
40 redirect_chain: Vec::new(),
41 timings: RequestTimings::default(),
42 compressed_size: None,
43 not_modified: status == StatusCode::NOT_MODIFIED,
44 }
45 }
46
47 pub fn content_type(&self) -> Option<String> {
49 self.headers
50 .get("content-type")
51 .and_then(|v| v.to_str().ok())
52 .map(|s| s.split(';').next().unwrap_or(s).trim().to_lowercase())
53 }
54
55 pub fn charset(&self) -> Option<String> {
57 self.headers
58 .get("content-type")
59 .and_then(|v| v.to_str().ok())
60 .and_then(|ct| {
61 ct.split(';')
62 .find(|part| part.trim().to_lowercase().starts_with("charset="))
63 .map(|part| part.split('=').nth(1).unwrap_or("utf-8").trim().to_lowercase())
64 })
65 }
66
67 pub fn etag(&self) -> Option<String> {
69 self.headers
70 .get("etag")
71 .and_then(|v| v.to_str().ok())
72 .map(String::from)
73 }
74
75 pub fn last_modified(&self) -> Option<String> {
77 self.headers
78 .get("last-modified")
79 .and_then(|v| v.to_str().ok())
80 .map(String::from)
81 }
82
83 pub fn cache_control(&self) -> Option<String> {
85 self.headers
86 .get("cache-control")
87 .and_then(|v| v.to_str().ok())
88 .map(String::from)
89 }
90
91 pub fn x_robots_tag(&self) -> Option<String> {
93 self.headers
94 .get("x-robots-tag")
95 .and_then(|v| v.to_str().ok())
96 .map(String::from)
97 }
98
99 pub fn headers_map(&self) -> HashMap<String, String> {
101 self.headers
102 .iter()
103 .filter_map(|(name, value)| {
104 value.to_str().ok().map(|v| (name.to_string(), v.to_string()))
105 })
106 .collect()
107 }
108
109 pub fn body_size(&self) -> u64 {
111 self.body.len() as u64
112 }
113
114 pub fn is_success(&self) -> bool {
116 self.status.is_success()
117 }
118
119 pub fn is_client_error(&self) -> bool {
121 self.status.is_client_error()
122 }
123
124 pub fn is_server_error(&self) -> bool {
126 self.status.is_server_error()
127 }
128
129 pub fn is_rate_limited(&self) -> bool {
131 self.status == StatusCode::TOO_MANY_REQUESTS
132 }
133
134 pub fn is_forbidden(&self) -> bool {
136 self.status == StatusCode::FORBIDDEN
137 }
138
139 pub fn text(&self) -> Result<String, std::string::FromUtf8Error> {
141 String::from_utf8(self.body.to_vec())
142 }
143}