use std::fs; use std::io::{self, BufRead}; use std::mem; use std::path::PathBuf; use anyhow::Error; use hyper::Uri; use structopt::StructOpt; mod download; use download::download; #[derive(StructOpt, Debug)] #[structopt(author, about)] struct Args { /// Read URLs from a file #[structopt(short, long)] input_file: Option, /// Specify output filename (will cause an error if multiple URLs are given) #[structopt(short = "O", long)] output_document: Option, /// Set output directory #[structopt(short = "P", long, default_value = ".")] directory_prefix: PathBuf, /// User-Agent header to include #[structopt(short = "U", long, default_value = concat!("webget/", env!("CARGO_PKG_VERSION")))] user_agent: String, /// The URLs to download urls: Vec, } #[tokio::main] async fn main() -> Result<(), Vec> { let (args, urls) = { let mut args: Args = Args::from_args(); let mut urls = mem::take(&mut args.urls); if let Some(input_file) = &args.input_file { if let Ok(input_file) = fs::File::open(input_file) { let input_file = io::BufReader::new(input_file); let input_file_urls = input_file .lines() .filter_map(|line| line.ok().and_then(|line| line.parse().ok())); urls.extend(input_file_urls); } } (args, urls) }; if urls.len() != 1 && args.output_document.is_some() { panic!("can't use the same output file for multiple URLs!") } let download_handles = urls .into_iter() .map(|url| download(url, &args)); let downloads = futures::future::join_all(download_handles); let download_errors = downloads.await .into_iter() .filter_map(|x| x.err()) .collect::>(); if download_errors.len() > 0 { Err(download_errors) } else { Ok(()) } }