use bytes::Bytes;
use reqwest::header::HeaderMap;
use reqwest::StatusCode;
use std::collections::HashMap;
use url::Url;
use crate::types::provenance::{RedirectHop, RequestTimings};
#[derive(Debug)]
pub struct FetchResponse {
pub final_url: Url,
pub status: StatusCode,
pub headers: HeaderMap,
pub body: Bytes,
pub redirect_chain: Vec<RedirectHop>,
pub timings: RequestTimings,
pub compressed_size: Option<u64>,
pub not_modified: bool,
}
impl FetchResponse {
pub fn new(final_url: Url, status: StatusCode, headers: HeaderMap, body: Bytes) -> Self {
Self {
final_url,
status,
headers,
body,
redirect_chain: Vec::new(),
timings: RequestTimings::default(),
compressed_size: None,
not_modified: status == StatusCode::NOT_MODIFIED,
}
}
pub fn content_type(&self) -> Option<String> {
self.headers
.get("content-type")
.and_then(|v| v.to_str().ok())
.map(|s| s.split(';').next().unwrap_or(s).trim().to_lowercase())
}
pub fn charset(&self) -> Option<String> {
self.headers
.get("content-type")
.and_then(|v| v.to_str().ok())
.and_then(|ct| {
ct.split(';')
.find(|part| part.trim().to_lowercase().starts_with("charset="))
.map(|part| part.split('=').nth(1).unwrap_or("utf-8").trim().to_lowercase())
})
}
pub fn etag(&self) -> Option<String> {
self.headers
.get("etag")
.and_then(|v| v.to_str().ok())
.map(String::from)
}
pub fn last_modified(&self) -> Option<String> {
self.headers
.get("last-modified")
.and_then(|v| v.to_str().ok())
.map(String::from)
}
pub fn cache_control(&self) -> Option<String> {
self.headers
.get("cache-control")
.and_then(|v| v.to_str().ok())
.map(String::from)
}
pub fn x_robots_tag(&self) -> Option<String> {
self.headers
.get("x-robots-tag")
.and_then(|v| v.to_str().ok())
.map(String::from)
}
pub fn headers_map(&self) -> HashMap<String, String> {
self.headers
.iter()
.filter_map(|(name, value)| {
value.to_str().ok().map(|v| (name.to_string(), v.to_string()))
})
.collect()
}
pub fn body_size(&self) -> u64 {
self.body.len() as u64
}
pub fn is_success(&self) -> bool {
self.status.is_success()
}
pub fn is_client_error(&self) -> bool {
self.status.is_client_error()
}
pub fn is_server_error(&self) -> bool {
self.status.is_server_error()
}
pub fn is_rate_limited(&self) -> bool {
self.status == StatusCode::TOO_MANY_REQUESTS
}
pub fn is_forbidden(&self) -> bool {
self.status == StatusCode::FORBIDDEN
}
pub fn text(&self) -> Result<String, std::string::FromUtf8Error> {
String::from_utf8(self.body.to_vec())
}
}