use std::time::Duration;
use anyhow::Result;
use colored::Colorize;
#[derive(Debug, Clone)]
pub struct HttpClient {
agent: ureq::Agent,
verbose: bool,
retries: u8,
}
impl HttpClient {
pub fn new(verbose: bool, retries: u8) -> Result<Self> {
let agent = ureq::Agent::config_builder()
.timeout_connect(Some(Duration::from_secs(15)))
.timeout_global(Some(Duration::from_secs(300)))
.user_agent(format!("gvsn/{}", env!("CARGO_PKG_VERSION")))
.build()
.new_agent();
Ok(Self {
agent,
verbose,
retries,
})
}
pub fn agent(&self) -> &ureq::Agent {
&self.agent
}
pub fn is_verbose(&self) -> bool {
self.verbose
}
pub fn retries(&self) -> u8 {
self.retries
}
}
pub fn log_request(client: &HttpClient, method: &str, url: &str) {
if client.is_verbose() {
eprintln!(" {} > {} {}", "[v]".dimmed(), method.bold(), url);
}
}
pub fn log_response(
client: &HttpClient,
status: u16,
reason: &str,
headers: &ureq::http::HeaderMap,
) {
if client.is_verbose() {
eprintln!(
" {} < {} {}",
"[v]".dimmed(),
status.to_string().bold(),
reason
);
for (name, value) in headers {
eprintln!(
" {} < {}: {}",
"[v]".dimmed(),
name,
value.to_str().unwrap_or("<binary>")
);
}
eprintln!();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_client_stores_verbose_and_retries() {
let client = HttpClient::new(true, 5).unwrap();
assert!(client.is_verbose());
assert_eq!(client.retries(), 5);
let client = HttpClient::new(false, 0).unwrap();
assert!(!client.is_verbose());
assert_eq!(client.retries(), 0);
}
#[test]
fn agent_is_accessible() {
let client = HttpClient::new(false, 3).unwrap();
let _agent: &ureq::Agent = client.agent();
}
#[test]
fn log_request_and_response_are_silent_when_not_verbose() {
let client = HttpClient::new(false, 3).unwrap();
log_request(&client, "GET", "https://example.invalid/");
let headers = ureq::http::HeaderMap::new();
log_response(&client, 200, "OK", &headers);
}
#[test]
fn log_request_and_response_do_not_panic_when_verbose() {
let client = HttpClient::new(true, 3).unwrap();
log_request(&client, "GET", "https://example.invalid/");
let mut headers = ureq::http::HeaderMap::new();
headers.insert(
ureq::http::header::CONTENT_TYPE,
"application/json".parse().unwrap(),
);
log_response(&client, 404, "Not Found", &headers);
}
}