use anyhow::Result;
use bytes::Bytes;
use futures::{join, StreamExt};
use tokio::time::{sleep, Duration};
use std::{
collections::HashMap,
fmt::Debug,
path::Path,
};
#[derive(Debug)]
pub struct API {
pub client_builder: reqwest::ClientBuilder,
pub headers: reqwest::header::HeaderMap,
pub requests: usize,
pub seconds: u64,
}
impl API {
pub fn new(requests: usize, seconds: u64) -> Self {
API {
client_builder: reqwest::ClientBuilder::new(),
headers: reqwest::header::HeaderMap::new(),
requests: 1,
seconds: 1,
}
}
pub async fn get_vec(
&self,
urls: Vec<String>,
dir: &str,
name_map: Option<HashMap<String, String>>
) -> Result<()> {
let mut count = 0;
let mut x = 0;
let mut y = 0;
while y < urls.len() {
let timer = async { sleep(Duration::from_secs(self.seconds)).await; };
let iter = async {
x = count * self.requests;
y = count * self.requests + self.requests;
let slice = &urls[x..y];
let client = reqwest::ClientBuilder::new()
.default_headers(self.headers.clone())
.build()
.expect("failed to build client");
futures::stream::iter(slice.iter().map(|url| {
let name_map = name_map.clone();
let future = client
.get(url)
.send();
async move {
match future.await {
Ok(resp) => {
match resp.bytes().await {
Ok(bytes) => API::write(&url, bytes, dir, name_map).await,
Err(_) => eprintln!("failed to download {url:#?}"),
}
},
Err(_) => eprintln!("failed to GET {url:#?}"),
}
}
}))
.buffer_unordered(self.requests)
.collect::<Vec<()>>()
.await;
};
join!(timer, iter);
count += 1;
}
Ok(())
}
pub async fn write(
url: &String,
bytes: Bytes,
dir: &str,
name_map: Option<HashMap<String, String>>
) -> () {
let file_name = match name_map {
Some(names) => {
let name_ref = names.get(url).expect("file name provided");
String::from(name_ref)
},
None => {
let name_ref = Path::new(url)
.file_name()
.expect("retrieved a file name")
.to_str()
.expect("&str of OsStr");
String::from(name_ref)
},
};
let file_path = Path::new(dir).join(file_name);
let _ = tokio::fs::write(&file_path, bytes).await;
}
}