use std::{
fs::File,
io::{self, BufReader, BufWriter},
time::Duration,
};
use anyhow::Context;
use clap::Parser;
use indicatif::{ProgressBar, ProgressFinish, ProgressStyle};
use crate::cli::Opt;
#[allow(clippy::too_many_lines)]
pub fn run() -> anyhow::Result<()> {
let opt = Opt::parse();
if let Some(shell) = opt.generate_completion {
Opt::print_completion(shell);
return Ok(());
}
let size = opt.size.map(u64::from);
let interval = opt
.interval
.map(Duration::try_from)
.transpose()
.context("the interval is outside the range of valid values")?;
let buffer_size = opt
.buffer_size
.map(usize::try_from)
.transpose()
.context("the buffer size is outside the range of valid values")?;
let inputs = opt
.inputs
.filter(|paths| paths.iter().all(|path| path.as_os_str() != "-"));
let mut keys = Vec::with_capacity(7);
if opt.name.is_some() {
keys.push("{prefix:.bold}:");
}
keys.push("{spinner:.green}");
if !opt.no_bytes {
if opt.si {
keys.push("{decimal_bytes}");
} else {
keys.push("{binary_bytes}");
}
}
if !opt.no_timer {
keys.push("{elapsed_precise}");
}
if !opt.no_rate {
if opt.si {
keys.push("[{decimal_bytes_per_sec}]");
} else {
keys.push("[{binary_bytes_per_sec}]");
}
}
let stdout = io::stdout().lock();
let writer = if let Some(buffer_size) = buffer_size {
BufWriter::with_capacity(buffer_size, stdout)
} else {
BufWriter::new(stdout)
};
if let Some(paths) = inputs {
let mut total_len = u64::default();
let mut files = Vec::with_capacity(paths.len());
for path in paths {
let file =
File::open(&path).with_context(|| format!("could not open {}", path.display()))?;
let metadata = file
.metadata()
.context("could not query metadata about a file")?;
total_len += metadata.len();
files.push(file);
}
let total_size = size.unwrap_or(total_len);
if !opt.no_progress {
keys.push("{wide_bar:.cyan/blue} {percent}%");
}
if !opt.no_eta {
keys.push("ETA {eta}");
}
let template = keys.join(" ");
let style = ProgressStyle::with_template(&template)?;
let pb = ProgressBar::new(total_size)
.with_style(style)
.with_finish(ProgressFinish::AndLeave);
if let Some(interval) = interval {
pb.enable_steady_tick(interval);
}
if let Some(name) = opt.name {
pb.set_prefix(name);
}
let mut writer = pb.wrap_write(writer);
for file in files {
let mut reader = if let Some(buffer_size) = buffer_size {
BufReader::with_capacity(buffer_size, file)
} else {
BufReader::new(file)
};
io::copy(&mut reader, &mut writer)
.context("could not copy the contents of a file to standard output")?;
}
} else {
if !opt.no_progress && size.is_some() {
keys.push("{wide_bar:.cyan/blue} {percent}%");
}
if !opt.no_eta && size.is_some() {
keys.push("ETA {eta}");
}
let template = keys.join(" ");
let style = ProgressStyle::with_template(&template)?;
let pb = size
.map_or_else(ProgressBar::no_length, ProgressBar::new)
.with_style(style)
.with_finish(ProgressFinish::AndLeave);
if let Some(interval) = interval {
pb.enable_steady_tick(interval);
}
if let Some(name) = opt.name {
pb.set_prefix(name);
}
let mut writer = pb.wrap_write(writer);
let stdin = io::stdin().lock();
let mut reader = if let Some(buffer_size) = buffer_size {
BufReader::with_capacity(buffer_size, stdin)
} else {
BufReader::new(stdin)
};
io::copy(&mut reader, &mut writer)
.context("could not copy the contents of standard input to standard output")?;
}
Ok(())
}