use super::{super::string::*, units::*};
use {
num_traits::cast,
std::{fmt, num::*, str::*},
};
#[derive(Clone, Copy, Debug, Default)]
pub struct ByteCount(pub u64);
impl ByteCount {
pub const fn from_bytes(bytes: u64) -> Self {
Self(bytes)
}
pub const fn from_kibibytes(kibibytes: u64) -> Self {
Self(kibibytes * KIBI)
}
pub const fn from_mebibytes(mebibytes: u64) -> Self {
Self(mebibytes * MEBI)
}
pub const fn from_gibibytes(gibibytes: u64) -> Self {
Self(gibibytes * GIBI)
}
pub const fn from_tebibytes(tebibytes: u64) -> Self {
Self(tebibytes * TEBI)
}
fn split(representation: &str) -> Option<(&str, &str)> {
for (index, c) in representation.char_indices().rev() {
if c == ' ' {
return Some((&representation[..index], &representation[index + 1..]));
}
if c.is_ascii_digit() {
let index = index + 1;
return Some((&representation[..index], &representation[index..]));
}
}
None
}
}
impl From<u64> for ByteCount {
fn from(bytes: u64) -> Self {
Self(bytes)
}
}
impl From<ByteCount> for u64 {
fn from(byte_count: ByteCount) -> Self {
byte_count.0
}
}
impl From<ByteCount> for usize {
fn from(byte_count: ByteCount) -> Self {
cast::<_, usize>(byte_count.0).expect("byte count as usize")
}
}
impl FromStr for ByteCount {
type Err = ParseError;
fn from_str(representation: &str) -> Result<Self, Self::Err> {
match Self::split(representation) {
Some((number, unit)) => {
let unit = parse_metric_unit(unit)?;
let integer: Result<u64, _> = number.parse();
match integer {
Ok(integer) => {
let Some(count) = integer.checked_mul(unit) else {
return Err(format!("cannot multiply: {} * {}", integer, unit).into());
};
Ok(count.into())
}
Err(_) => {
let float: f64 = number.parse().map_err(|error: ParseFloatError| error.to_string())?;
if float >= 0.0 {
let Some(unit) = cast::<_, f64>(unit) else {
return Err(format!("cannot cast to float: {}", unit).into());
};
let count = (float * unit).round();
let Some(count) = cast::<_, u64>(count) else {
return Err(format!("cannot cast to unsigned integer: {}", count).into());
};
Ok(count.into())
} else {
Err(format!("cannot be negative: {}", float).into())
}
}
}
}
None => Ok(Self(representation.parse().map_err(|error: ParseIntError| error.to_string())?)),
}
}
}
impl fmt::Display for ByteCount {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{} bytes", self.0)
}
}