use std::fmt;
use std::io::{BufRead, Write};
pub const DEFAULT_LEVEL: i32 = 2;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Progress {
Auto,
Always,
Never,
}
impl Progress {
pub fn shown(self, verbosity: i32, stderr_is_terminal: bool) -> bool {
match self {
Self::Always => true,
Self::Never => false,
Self::Auto => verbosity >= DEFAULT_LEVEL && stderr_is_terminal,
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct HumanSize {
value: f64,
precision: usize,
suffix: &'static str,
}
impl HumanSize {
pub fn new(bytes: u64, verbose: bool) -> Self {
if verbose {
return if bytes >= 1 << 53 {
Self {
value: bytes as f64 / (1u64 << 20) as f64,
precision: 2,
suffix: " MiB",
}
} else {
Self {
value: bytes as f64,
precision: 0,
suffix: " B",
}
};
}
const UNITS: [(u32, &str); 6] = [
(60, " EiB"),
(50, " PiB"),
(40, " TiB"),
(30, " GiB"),
(20, " MiB"),
(10, " KiB"),
];
let (value, suffix) = UNITS
.iter()
.find(|(shift, _)| bytes >= 1u64 << shift)
.map_or((bytes as f64, " B"), |(shift, suffix)| {
(bytes as f64 / (1u64 << shift) as f64, *suffix)
});
let precision = if value >= 100.0 || value as u64 as f64 == value {
0
} else if value >= 10.0 {
1
} else {
debug_assert!(value > 1.0);
2
};
Self {
value,
precision,
suffix,
}
}
#[cfg(test)]
pub fn value(&self) -> f64 {
self.value
}
#[cfg(test)]
pub fn precision(&self) -> usize {
self.precision
}
#[cfg(test)]
pub fn suffix(&self) -> &'static str {
self.suffix
}
pub fn columns(&self, width: usize) -> String {
format!(
"{:>width$.prec$}{:>4}",
self.value,
self.suffix,
width = width,
prec = self.precision
)
}
}
impl fmt::Display for HumanSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match f.width() {
Some(width) => write!(
f,
"{:>width$.prec$}{}",
self.value,
self.suffix,
width = width,
prec = self.precision
),
None => write!(
f,
"{:.prec$}{}",
self.value,
self.suffix,
prec = self.precision
),
}
}
}
pub fn confirm(
prompt: &str,
abort_message: &str,
stdin_is_input: bool,
input: &mut impl BufRead,
) -> bool {
let mut stderr = std::io::stderr().lock();
if stdin_is_input {
let _ = writeln!(stderr, "stdin is an input - not proceeding.");
return false;
}
let _ = write!(stderr, "{prompt}");
let _ = stderr.flush();
let mut answer = String::new();
let accepted =
input.read_line(&mut answer).is_ok() && matches!(answer.chars().next(), Some('y' | 'Y'));
if !accepted {
let _ = writeln!(stderr, "{abort_message}");
}
accepted
}
#[cfg(test)]
mod tests;