use std::borrow::Cow;
use http::{HeaderMap, Method, StatusCode, Uri};
use crate::{
Error,
file_info::FileInfo,
maybe_async::{Client, Response},
};
#[derive(Debug, Default)]
pub struct Head {
pub updated_uri: Option<Uri>,
pub remote_file_info: Option<FileInfo>,
pub filename: Option<String>,
}
#[derive(Default)]
pub struct LazyHead {
head: Option<Head>,
}
impl Head {
pub async fn create<C: Client>(client: &C, uri: &Uri, headers: &HeaderMap) -> Head {
let (updated_uri, head) = client.request(Method::HEAD, uri, headers.clone()).await;
if let Ok(response) = head {
Head::create_inner(response.status(), response.headers(), uri, updated_uri)
.unwrap_or_default()
} else {
Head::default()
}
}
fn create_inner(
status: StatusCode,
headers: &HeaderMap,
uri: &Uri,
updated_uri: Option<Uri>,
) -> Result<Self, Error> {
if !status.is_success() {
return Err(Error::UnexpectedStatus {
status: status.as_u16(),
});
}
let mut result = Self {
updated_uri,
remote_file_info: None,
filename: None,
};
result.remote_file_info = Some(FileInfo::from_response(status, headers, 0));
result.filename = crate::headers::parse_content_disposition(headers)
.map(Cow::<str>::into_owned)
.or_else(|| {
let url_filename = uri.path().split('/').next_back().unwrap();
if url_filename.is_empty() {
None
} else {
Some(url_filename.to_owned())
}
});
Ok(result)
}
pub fn get_remote_file_name(&self) -> &str {
self.filename.as_deref().unwrap_or("file")
}
pub fn get_remote_file_length(&self) -> Option<u64> {
self.remote_file_info
.as_ref()
.and_then(|info| info.file_length)
}
}
impl LazyHead {
pub async fn get<C: Client>(&mut self, client: &C, uri: &Uri, headers: &HeaderMap) -> &Head {
if self.head.is_none() {
let h = Head::create(client, uri, headers).await;
self.head.replace(h);
}
self.head.as_ref().unwrap()
}
pub fn try_get(&self) -> Option<&Head> {
self.head.as_ref()
}
}