use std::fmt::Write;
use std::time::Duration;
pub fn format_micros(micros: i32, f: &mut dyn std::fmt::Write) -> std::fmt::Result {
if micros < 1_000 {
write!(f, "{} us", micros)
} else if micros < 10_000 {
write!(f, "{:.2} ms", micros as f32 / 1_000.)
} else if micros < 100_000 {
write!(f, "{:.1} ms", micros as f32 / 1_000.)
} else if micros < 1_000_000 {
write!(f, "{} ms", micros / 1_000)
} else if micros < 10_000_000 {
write!(f, "{:.2} s", micros as f32 / 1_000_000.)
} else if micros < 100_000_000 {
write!(f, "{:.1} s", micros as f32 / 1_000_000.)
} else {
write!(f, "{} s", micros / 1_000_000)
}
}
pub fn format_short_duration(duration_in_micros: i32) -> String {
let mut result = String::new();
let _ = format_micros(duration_in_micros, &mut result);
result
}
pub fn format_bytes(size_in_bytes: usize, f: &mut dyn std::fmt::Write) -> std::fmt::Result {
if size_in_bytes == 1 {
return write!(f, "1 byte");
} else if size_in_bytes < 1024 {
return write!(f, "{} bytes", size_in_bytes);
}
let mut magnitude = 0;
let mut size = size_in_bytes as f32;
while size > 1024. && magnitude < 5 {
size /= 1024.;
magnitude += 1;
}
if size <= 10. {
write!(f, "{:.2} ", size)?;
} else if size <= 100. {
write!(f, "{:.1} ", size)?;
} else {
write!(f, "{:.0} ", size)?;
}
match magnitude {
0 => write!(f, "Bytes"),
1 => write!(f, "KiB"),
2 => write!(f, "MiB"),
3 => write!(f, "GiB"),
4 => write!(f, "TiB"),
_ => write!(f, "PiB"),
}
}
pub fn format_size(size_in_bytes: usize) -> String {
let mut result = String::new();
let _ = format_bytes(size_in_bytes, &mut result);
result
}
pub fn parse_size(str: impl AsRef<str>) -> anyhow::Result<usize> {
lazy_static::lazy_static! {
static ref NUMBER_AND_SUFFIX: regex::Regex =
regex::Regex::new(r"^ *(\d+) *([bBkKmMgGtT]?) *$").unwrap();
}
match NUMBER_AND_SUFFIX.captures(str.as_ref()) {
Some(captures) => {
let number = captures[1].parse::<usize>().unwrap();
match &captures[2] {
"k" | "K" => Ok(number * 1024),
"m" | "M" => Ok(number * 1024 * 1024),
"g" | "G" => Ok(number * 1024 * 1024 * 1024),
"t" | "T" => Ok(number * 1024 * 1024 * 1024 * 1024),
_ => Ok(number),
}
}
None => Err(anyhow::anyhow!(
"Cannot parse '{}' into a size expression.\
Expected a positive number and optionally 'b', 'k', 'm', 'g' or 't' as suffix.",
str.as_ref()
)),
}
}
pub fn parse_duration(str: impl AsRef<str>) -> anyhow::Result<Duration> {
lazy_static::lazy_static! {
static ref NUMBER_AND_SUFFIX: regex::Regex =
regex::Regex::new(r"^ *(\d+) *((ms|s|m|h|d|MS|S|M|H|D)?) *$").unwrap();
}
match NUMBER_AND_SUFFIX.captures(str.as_ref()) {
Some(captures) => {
let number = captures[1].parse::<u64>().unwrap();
match &captures[2] {
"s" | "S" => Ok(Duration::from_secs(number)),
"m" | "M" => Ok(Duration::from_secs(number * 60)),
"h" | "H" => Ok(Duration::from_secs(number * 60 * 60)),
"d" | "D" => Ok(Duration::from_secs(number * 60 * 60 * 24)),
_ => Ok(Duration::from_millis(number)),
}
}
None => Err(anyhow::anyhow!(
"Cannot parse '{}' into a duration expression.\
Expected a positive number an optionally 'ms', 's', 'm', 'h' or 'd' as suffix.",
str.as_ref()
)),
}
}
pub fn format_duration(duration: Duration) -> String {
let mut result = String::new();
let mut value = duration.as_millis();
{
let days = value / (1000 * 60 * 60 * 24);
if days > 0 {
let _ = write!(result, "{}d", days);
value %= 1000 * 60 * 60 * 24;
}
}
{
let hours = value / (1000 * 60 * 60);
if hours > 0 {
if !result.is_empty() {
result.push(' ');
}
let _ = write!(result, "{}h", hours);
value %= 1000 * 60 * 60;
}
}
{
let minutes = value / (1000 * 60);
if minutes > 0 {
if !result.is_empty() {
result.push(' ');
}
let _ = write!(result, "{}m", minutes);
value %= 1000 * 60;
}
}
{
let seconds = value / 1000;
if seconds > 0 {
if !result.is_empty() {
result.push(' ');
}
let _ = write!(result, "{}s", seconds);
value %= 1000;
}
}
if value > 0 {
if !result.is_empty() {
result.push(' ');
}
let _ = write!(result, "{}ms", value);
}
result
}