#![doc = include_str!("../README.md")]
use {
anyhow::Result,
bytes::Buf,
reqwest::Url,
std::{
fs::File,
path::{Path, PathBuf},
},
};
pub struct Client {
client: reqwest::blocking::Client,
}
impl Client {
pub fn new(user_agent: &str) -> Result<Client> {
Ok(Client {
client: reqwest::blocking::Client::builder()
.user_agent(user_agent)
.build()?,
})
}
pub fn to_string(&self, url: &str) -> Result<String> {
Ok(self.client.get(url).send()?.text()?)
}
pub fn to_file(&self, url: &str, dst: Option<&Path>) -> Result<PathBuf> {
let res = self.client.get(url).send()?;
let path = dst
.and_then(|x| {
if x.is_dir() {
Some(x.join(url_filename(res.url())))
} else {
None
}
})
.unwrap_or_else(|| PathBuf::from(url_filename(res.url())));
std::io::copy(&mut res.bytes()?.reader(), &mut File::create(&path)?)?;
Ok(path)
}
}
#[cfg(test)]
mod test {
use super::*;
use reqwest::Url;
#[test]
fn test_url_filename() {
println!();
for (url, filename) in [
("http://some.host.tld/path/to/file.ext", "file.ext"),
("http://some.host.tld/path/to/", "to"),
("http://some.host.tld/path/to", "to"),
("http://some.host.tld/path/", "path"),
("http://some.host.tld/path", "path"),
("http://some.host.tld/", "some.host.tld.html"),
("http://some.host.tld", "some.host.tld.html"),
] {
println!("* {url:?} => {filename:?}");
assert_eq!(url_filename(&Url::parse(url).unwrap()), *filename);
}
}
}
#[must_use]
pub fn url_filename(url: &Url) -> String {
url.path_segments()
.and_then(|x| {
for s in x.rev() {
if !s.is_empty() {
return Some(s.to_string());
}
}
None
})
.unwrap_or_else(|| format!("{}.html", url.host_str().unwrap()))
}