use std::fmt;
use std::time::Duration;
const API_TIMEOUT: Duration = Duration::from_secs(10);
const PAGE_TIMEOUT: Duration = Duration::from_secs(30);
const API_USER_AGENT: &str = "decruft/0.1";
const PAGE_USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36";
#[derive(Debug)]
pub enum FetchError {
Transport(ureq::Error),
Status(u16),
}
impl fmt::Display for FetchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Transport(e) => write!(f, "{e}"),
Self::Status(code) => write!(f, "HTTP {code}"),
}
}
}
fn build_agent(timeout: Duration) -> ureq::Agent {
let config = ureq::config::Config::builder()
.timeout_global(Some(timeout))
.http_status_as_error(false)
.build();
ureq::Agent::new_with_config(config)
}
pub(crate) fn get(url: &str) -> Option<String> {
let agent = build_agent(API_TIMEOUT);
let response = agent
.get(url)
.header("User-Agent", API_USER_AGENT)
.call()
.ok()?;
if response.status() != 200 {
return None;
}
response.into_body().read_to_string().ok()
}
pub(crate) fn get_with_headers(url: &str, headers: &[(&str, &str)]) -> Option<String> {
let agent = build_agent(API_TIMEOUT);
let mut request = agent.get(url).header("User-Agent", API_USER_AGENT);
for &(name, value) in headers {
request = request.header(name, value);
}
let response = request.call().ok()?;
if response.status() != 200 {
return None;
}
response.into_body().read_to_string().ok()
}
pub fn fetch_page(url: &str) -> Result<String, FetchError> {
let agent = build_agent(PAGE_TIMEOUT);
let response = agent
.get(url)
.header("User-Agent", PAGE_USER_AGENT)
.call()
.map_err(FetchError::Transport)?;
if response.status() != 200 {
return Err(FetchError::Status(response.status().as_u16()));
}
response
.into_body()
.read_to_string()
.map_err(FetchError::Transport)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fetch_page_returns_status_for_404() {
let result = fetch_page("https://httpbin.org/status/404");
if let Err(e) = result {
match e {
FetchError::Status(code) => assert_eq!(code, 404),
FetchError::Transport(_) => {
}
}
}
}
#[test]
fn fetch_page_returns_status_for_500() {
let result = fetch_page("https://httpbin.org/status/500");
if let Err(e) = result {
match e {
FetchError::Status(code) => assert_eq!(code, 500),
FetchError::Transport(_) => {}
}
}
}
#[test]
fn get_returns_none_for_404() {
let result = get("https://httpbin.org/status/404");
if result.is_some() {
}
}
}