1use std::collections::HashMap;
8use std::io::Error as IOError;
9use std::path::Path;
10
11use lazy_static::lazy_static;
12use reqwest::{Client as ReqwestClient, RequestBuilder};
13use reqwest::ClientBuilder;
14use reqwest::Error as ReqwestError;
15use reqwest::IntoUrl;
16use reqwest::Response;
17use reqwest::header::{HeaderMap, HeaderValue};
18use tokio::fs::File;
19use tokio::io::AsyncWriteExt;
20
21lazy_static! {
28 pub static ref USER_AGENT: String = format!(
29 "{}/{}",
30 env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")
31 );
32}
33
34pub fn get_user_agent () -> &'static str {
36 &USER_AGENT
37}
38
39pub struct Client {
45 client: ReqwestClient,
46}
47
48impl Client {
49 pub fn new(headers: Option<HeaderMap>) -> Self {
50 let client = ClientBuilder::new()
51 .default_headers(build_default_headers(headers))
52 .gzip(true)
53 .deflate(true)
54 .build()
55 .expect("Can't build HTTP client");
56
57 Self {
58 client,
59 }
60 }
61
62 pub async fn get<T>(&self, url: T) -> Result<Response, Error>
63 where T: IntoUrl {
64 let request = self.client.get(url);
65 self.run(request).await
66 }
67
68 pub async fn get_with_headers<T>(&self, url: T, headers: HashMap<String, String>) -> Result<Response, Error>
69 where T: IntoUrl {
70 let headers = parse_headers(headers);
71
72 let request = self.client
73 .get(url)
74 .headers(headers);
75
76 self.run(request).await
77 }
78
79 pub async fn run(&self, request: RequestBuilder) -> Result<Response, Error> {
80 request
81 .send()
82 .await
83 .map_err(|error| Error::Reqwest(error))
84 }
85
86 pub async fn download<P, T>(&self, url: T, target_path: P) -> Result<usize, Error>
87 where T: IntoUrl, P: AsRef<Path> {
88 let mut file = File::create(target_path)
89 .await
90 .map_err(|error| Error::IO(error))?;
91
92 let mut target_content = self.get(url).await?;
93 let mut bytes_read = 0;
94 while let Some(chunk) = target_content
95 .chunk()
96 .await
97 .map_err(|error| Error::Reqwest(error))?
98 {
99 bytes_read += file.write(chunk.as_ref())
100 .await
101 .map_err(|error| Error::IO(error))?;
102 }
103
104 Ok(bytes_read)
105 }
106}
107
108pub fn parse_headers(headers: HashMap<String, String>) -> HeaderMap {
113 headers
114 .iter()
115 .map(|(name, value)| (
116 name.parse().expect("Can't parse header name"),
117 value.parse().expect("Can't parse header value")
118 ))
119 .collect()
120}
121
122fn build_default_headers(headers: Option<HeaderMap>) -> HeaderMap {
123 let mut headers = headers
124 .unwrap_or(HeaderMap::new());
125
126 if !headers.contains_key("User-Agent") {
128 headers.append("User-Agent", HeaderValue::from_static(get_user_agent()));
129 }
130
131 headers
132}
133
134#[derive(Debug)]
140pub enum Error {
141 Reqwest(ReqwestError),
143
144 IO(IOError),
146}