use std::{fmt, ops};
use super::Factor;
pub trait ByteUnits {
fn bytes(self) -> ByteLength;
fn kibibytes(self) -> ByteLength;
fn kilobytes(self) -> ByteLength;
fn mebibytes(self) -> ByteLength;
fn megabytes(self) -> ByteLength;
fn gibibytes(self) -> ByteLength;
fn gigabytes(self) -> ByteLength;
fn tebibytes(self) -> ByteLength;
fn terabytes(self) -> ByteLength;
}
impl ByteUnits for usize {
fn bytes(self) -> ByteLength {
ByteLength(self)
}
fn kibibytes(self) -> ByteLength {
ByteLength::from_kibi(self)
}
fn kilobytes(self) -> ByteLength {
ByteLength::from_kilo(self)
}
fn mebibytes(self) -> ByteLength {
ByteLength::from_mebi(self)
}
fn megabytes(self) -> ByteLength {
ByteLength::from_mega(self)
}
fn gibibytes(self) -> ByteLength {
ByteLength::from_gibi(self)
}
fn gigabytes(self) -> ByteLength {
ByteLength::from_giga(self)
}
fn tebibytes(self) -> ByteLength {
ByteLength::from_tebi(self)
}
fn terabytes(self) -> ByteLength {
ByteLength::from_tera(self)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct ByteLength(pub usize);
impl From<usize> for ByteLength {
fn from(value: usize) -> Self {
Self(value)
}
}
impl ops::Add for ByteLength {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl ops::AddAssign for ByteLength {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl ops::Sub for ByteLength {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl ops::SubAssign for ByteLength {
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
}
}
impl ByteLength {
pub fn bytes(&self) -> usize {
self.0
}
fn scaled(self, scale: f64) -> f64 {
self.0 as f64 / scale
}
pub fn kibis(self) -> f64 {
self.scaled(1024.0)
}
pub fn kilos(self) -> f64 {
self.scaled(1000.0)
}
pub fn mebis(self) -> f64 {
self.scaled(1024.0f64.powi(2))
}
pub fn megas(self) -> f64 {
self.scaled(1000.0f64.powi(2))
}
pub fn gibis(self) -> f64 {
self.scaled(1024.0f64.powi(3))
}
pub fn gigas(self) -> f64 {
self.scaled(1000.0f64.powi(3))
}
pub fn tebis(self) -> f64 {
self.scaled(1024.0f64.powi(4))
}
pub fn teras(self) -> f64 {
self.scaled(1000.0f64.powi(4))
}
pub const MAX: ByteLength = ByteLength(usize::MAX);
pub const fn saturating_add(self, rhs: ByteLength) -> ByteLength {
ByteLength(self.0.saturating_add(rhs.0))
}
pub const fn saturating_sub(self, rhs: ByteLength) -> ByteLength {
ByteLength(self.0.saturating_sub(rhs.0))
}
pub const fn saturating_mul(self, rhs: ByteLength) -> ByteLength {
ByteLength(self.0.saturating_mul(rhs.0))
}
pub const fn wrapping_add(self, rhs: ByteLength) -> ByteLength {
ByteLength(self.0.wrapping_add(rhs.0))
}
pub const fn wrapping_sub(self, rhs: ByteLength) -> ByteLength {
ByteLength(self.0.wrapping_sub(rhs.0))
}
pub const fn wrapping_mul(self, rhs: ByteLength) -> ByteLength {
ByteLength(self.0.wrapping_mul(rhs.0))
}
pub const fn wrapping_div(self, rhs: ByteLength) -> ByteLength {
ByteLength(self.0.wrapping_div(rhs.0))
}
pub fn checked_add(self, rhs: ByteLength) -> Option<ByteLength> {
self.0.checked_add(rhs.0).map(ByteLength)
}
pub fn checked_sub(self, rhs: ByteLength) -> Option<ByteLength> {
self.0.checked_sub(rhs.0).map(ByteLength)
}
pub fn checked_mul(self, rhs: ByteLength) -> Option<ByteLength> {
self.0.checked_mul(rhs.0).map(ByteLength)
}
pub fn checked_div(self, rhs: ByteLength) -> Option<ByteLength> {
self.0.checked_div(rhs.0).map(ByteLength)
}
}
impl ByteLength {
pub const fn from_byte(bytes: usize) -> Self {
ByteLength(bytes)
}
const fn new(value: usize, scale: usize) -> Self {
ByteLength(value.saturating_mul(scale))
}
pub const fn from_kibi(kibi_bytes: usize) -> Self {
Self::new(kibi_bytes, 1024)
}
pub const fn from_kilo(kibi_bytes: usize) -> Self {
Self::new(kibi_bytes, 1000)
}
pub const fn from_mebi(mebi_bytes: usize) -> Self {
Self::new(mebi_bytes, 1024usize.pow(2))
}
pub const fn from_mega(mebi_bytes: usize) -> Self {
Self::new(mebi_bytes, 1000usize.pow(2))
}
pub const fn from_gibi(gibi_bytes: usize) -> Self {
Self::new(gibi_bytes, 1024usize.pow(3))
}
pub const fn from_giga(giga_bytes: usize) -> Self {
Self::new(giga_bytes, 1000usize.pow(3))
}
pub const fn from_tebi(gibi_bytes: usize) -> Self {
Self::new(gibi_bytes, 1024usize.pow(4))
}
pub const fn from_tera(tera_bytes: usize) -> Self {
Self::new(tera_bytes, 1000usize.pow(4))
}
}
impl ByteLength {
pub fn max(self, other: Self) -> Self {
Self(self.0.max(other.0))
}
pub fn min(self, other: Self) -> Self {
Self(self.0.min(other.0))
}
}
impl fmt::Debug for ByteLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_tuple("ByteLength").field(&self.0).finish()
} else {
write!(f, "ByteLength({self})")
}
}
}
impl fmt::Display for ByteLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
if self.0 >= 1024usize.pow(4) {
write!(f, "{:.2} tebibytes", self.tebis())
} else if self.0 >= 1024usize.pow(3) {
write!(f, "{:.2} gibibytes", self.gibis())
} else if self.0 >= 1024usize.pow(2) {
write!(f, "{:.2} mebibytes", self.mebis())
} else if self.0 >= 1024 {
write!(f, "{:.2} kibibytes", self.kibis())
} else {
write!(f, "{} bytes", self.bytes())
}
} else if self.0 >= 1000usize.pow(4) {
write!(f, "{:.2} terabytes", self.teras())
} else if self.0 >= 1000usize.pow(3) {
write!(f, "{:.2} gigabytes", self.gigas())
} else if self.0 >= 1000usize.pow(2) {
write!(f, "{:.2} megabytes", self.megas())
} else if self.0 >= 1000 {
write!(f, "{:.2} kilobytes", self.kilos())
} else {
write!(f, "{} bytes", self.bytes())
}
}
}
impl<S: Into<Factor>> ops::Mul<S> for ByteLength {
type Output = Self;
fn mul(mut self, rhs: S) -> Self {
self.0 = (self.0 as f64 * rhs.into().0 as f64) as usize;
self
}
}
impl<S: Into<Factor>> ops::MulAssign<S> for ByteLength {
fn mul_assign(&mut self, rhs: S) {
*self = *self * rhs;
}
}
impl<S: Into<Factor>> ops::Div<S> for ByteLength {
type Output = Self;
fn div(mut self, rhs: S) -> Self {
self.0 = (self.0 as f64 / rhs.into().0 as f64) as usize;
self
}
}
impl<S: Into<Factor>> ops::DivAssign<S> for ByteLength {
fn div_assign(&mut self, rhs: S) {
*self = *self / rhs;
}
}