From b0383454f00b8d30390df6bb000329f48f2901ca Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Tue, 16 Dec 2025 16:24:53 -0500 Subject: [PATCH] feat(types): Add utility initialization functions for StorageSize such as StorageSize::kb(324) --- harmony_types/Cargo.toml | 1 + harmony_types/src/storage.rs | 156 ++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 1 deletion(-) diff --git a/harmony_types/Cargo.toml b/harmony_types/Cargo.toml index f02874e..56b6cdf 100644 --- a/harmony_types/Cargo.toml +++ b/harmony_types/Cargo.toml @@ -9,3 +9,4 @@ license.workspace = true serde.workspace = true url.workspace = true rand.workspace = true +serde_json.workspace = true diff --git a/harmony_types/src/storage.rs b/harmony_types/src/storage.rs index bd5fd95..a0a3094 100644 --- a/harmony_types/src/storage.rs +++ b/harmony_types/src/storage.rs @@ -1,6 +1,160 @@ use serde::{Deserialize, Serialize}; +use std::fmt; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Debug)] pub struct StorageSize { size_bytes: u64, + #[serde(skip)] + display_suffix: Option, +} + +impl StorageSize { + pub fn new(size_bytes: u64) -> Self { + Self { + size_bytes, + display_suffix: None, + } + } + + pub fn b(size: u64) -> Self { + Self { + size_bytes: size, + display_suffix: Some("B".to_string()), + } + } + + pub fn kb(size: u64) -> Self { + Self { + size_bytes: size * 1024, + display_suffix: Some("KB".to_string()), + } + } + + pub fn mb(size: u64) -> Self { + Self { + size_bytes: size * 1024 * 1024, + display_suffix: Some("MB".to_string()), + } + } + + pub fn gb(size: u64) -> Self { + Self { + size_bytes: size * 1024 * 1024 * 1024, + display_suffix: Some("GB".to_string()), + } + } + + pub fn gi(size: u64) -> Self { + Self { + size_bytes: size * 1024 * 1024 * 1024, + display_suffix: Some("GiB".to_string()), + } + } + + pub fn tb(size: u64) -> Self { + Self { + size_bytes: size * 1024 * 1024 * 1024 * 1024, + display_suffix: Some("TB".to_string()), + } + } + + pub fn ti(size: u64) -> Self { + Self { + size_bytes: size * 1024 * 1024 * 1024 * 1024, + display_suffix: Some("TiB".to_string()), + } + } + + pub fn bytes(&self) -> u64 { + self.size_bytes + } +} + +impl fmt::Display for StorageSize { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(suffix) = &self.display_suffix { + write!(f, "{}{}", self.size_bytes, suffix) + } else { + write!(f, "{}B", self.size_bytes) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bytes() { + let size = StorageSize::b(123); + assert_eq!(size.bytes(), 123); + assert_eq!(size.to_string(), "123B"); + } + + #[test] + fn test_kilobytes() { + let size = StorageSize::kb(2); + assert_eq!(size.bytes(), 2048); + assert_eq!(size.to_string(), "2048KB"); + } + + #[test] + fn test_megabytes() { + let size = StorageSize::mb(3); + assert_eq!(size.bytes(), 3 * 1024 * 1024); + assert_eq!(size.to_string(), "3145728MB"); + } + + #[test] + fn test_gigabytes() { + let size = StorageSize::gb(4); + assert_eq!(size.bytes(), 4 * 1024 * 1024 * 1024); + assert_eq!(size.to_string(), "4294967296GB"); + } + + #[test] + fn test_gibibytes() { + let size = StorageSize::gi(1); + assert_eq!(size.bytes(), 1024 * 1024 * 1024); + assert_eq!(size.to_string(), "1073741824GiB"); + } + + #[test] + fn test_terabytes() { + let size = StorageSize::tb(5); + assert_eq!(size.bytes(), 5 * 1024 * 1024 * 1024 * 1024); + assert_eq!(size.to_string(), "5497558138880TB"); + } + + #[test] + fn test_tebibytes() { + let size = StorageSize::ti(1); + assert_eq!(size.bytes(), 1024 * 1024 * 1024 * 1024); + assert_eq!(size.to_string(), "1099511627776TiB"); + } + + #[test] + fn test_new_without_suffix() { + let size = StorageSize::new(999); + assert_eq!(size.bytes(), 999); + assert_eq!(size.to_string(), "999B"); + } + + #[test] + fn test_serde_roundtrip() { + let original = StorageSize::gi(1); + let serialized = serde_json::to_string(&original).unwrap(); + let deserialized: StorageSize = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(original.bytes(), deserialized.bytes()); + // Note: suffix is lost during serialization/deserialization + assert_ne!(original.to_string(), deserialized.to_string()); + } + + #[test] + fn test_ord() { + let one_gb = StorageSize::gb(1); + let one_gi = StorageSize::gi(1); + assert!(one_gb < one_gi); // 1GB = 1000MB, 1GiB = 1024MB + } }