use std::path::{Path, PathBuf}; use anyhow::{Error, Context}; use fehler::throws; use hyper::Uri; use hyper::body::HttpBody; use hyper::client::Client; use hyper::header::{CONTENT_TYPE, LOCATION}; use tokio::fs; use tokio::io::AsyncWriteExt; use super::Args; #[throws] pub(crate) async fn download(url: Uri, args: &Args) { let output_file_path = if let Some(output) = &args.output_document { Some(output.clone()) } else { let url_path = Path::new(url.path()); match (url_path.file_name(), url.path().ends_with('/')) { (Some(file_name), false) => Some(PathBuf::from(file_name)), _ => None, } }; let client = Client::new(); let mut response = client.get(url.clone()).await?; while response.status().is_redirection() { let location = response.headers() .get(LOCATION).context("no Location header in redirect")? .to_str().context("malformed Location header in redirect")? .parse().context("non-URL Location header in redirect")?; response = client.get(location).await?; } if !response.status().is_success() { panic!("non-success response code {} in URL {}", response.status(), url); } let output_file_path = if let Some(path) = output_file_path { path } else { let content_type = response.headers().get(CONTENT_TYPE); let extension = content_type .and_then(|mime_type| mime_type.to_str().ok()) .and_then(|mime_type| mime2ext::mime2ext(mime_type)) .map(|x| format!(".{}", x)) .unwrap_or_default(); PathBuf::from(format!("index{}", extension)) }; let mut output_file = fs::File::create(output_file_path).await.expect("couldn't open output file!"); while let Some(data) = response.body_mut().data().await { output_file.write_all(&data?).await?; } }