use std::ops::Mul; #[derive(thiserror::Error, Debug)] enum FilesSizeUtilsError { #[error("UnitConvertError: {0}")] UnitConvert(String), } /// Holds a data size, convertible in any form #[derive( serde::Serialize, serde::Deserialize, Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Default, )] pub struct FileSize(usize); impl FileSize { pub const fn from_bytes(size: usize) -> Self { Self(size) } pub const fn from_mb(mb: usize) -> Self { Self(mb * 1000 * 1000) } pub const fn from_gb(gb: usize) -> Self { Self(gb * 1000 * 1000 * 1000) } /// Convert size unit to MB pub fn from_size_unit(unit: &str, value: usize) -> anyhow::Result { let fact = match unit { "bytes" | "b" => 1f64, "KB" => 1000f64, "MB" => 1000f64 * 1000f64, "GB" => 1000f64 * 1000f64 * 1000f64, "TB" => 1000f64 * 1000f64 * 1000f64 * 1000f64, "k" | "KiB" => 1024f64, "M" | "MiB" => 1024f64 * 1024f64, "G" | "GiB" => 1024f64 * 1024f64 * 1024f64, "T" | "TiB" => 1024f64 * 1024f64 * 1024f64 * 1024f64, _ => { return Err( FilesSizeUtilsError::UnitConvert(format!("Unknown size unit: {unit}")).into(), ); } }; Ok(Self((value as f64).mul(fact).ceil() as usize)) } /// Get file size as bytes pub fn as_bytes(&self) -> usize { self.0 } /// Get file size as megabytes pub fn as_mb(&self) -> usize { self.0 / (1000 * 1000) } } #[cfg(test)] mod tests { use crate::utils::file_size_utils::FileSize; #[test] fn convert_units_mb() { assert_eq!(FileSize::from_size_unit("MB", 1).unwrap().as_mb(), 1); assert_eq!(FileSize::from_size_unit("MB", 1000).unwrap().as_mb(), 1000); assert_eq!( FileSize::from_size_unit("GB", 1000).unwrap().as_mb(), 1000 * 1000 ); assert_eq!(FileSize::from_size_unit("GB", 1).unwrap().as_mb(), 1000); assert_eq!(FileSize::from_size_unit("GiB", 3).unwrap().as_mb(), 3221); assert_eq!( FileSize::from_size_unit("KiB", 488281).unwrap().as_mb(), 499 ); } }