pub enum MemoryUnits {
    B(usize),
    KiB(usize),
    MiB(usize),
    GiB(usize),
}

impl MemoryUnits {
    pub const fn to_bytes(&self) -> usize {
        match self {
            Self::B(b) => *b,
            Self::KiB(kib) => *kib * 1024,
            Self::MiB(mib) => *mib * 1024 * 1024,
            Self::GiB(gib) => *gib * 1024 * 1024 * 1024,
        }
    }

    pub const fn from_bytes(bytes: usize) -> Self {
        if bytes < 1024 {
            Self::B(bytes)
        } else if bytes < 1024 * 1024 {
            Self::KiB(bytes / 1024)
        } else if bytes < 1024 * 1024 * 1024 {
            Self::MiB(bytes / (1024 * 1024))
        } else {
            Self::GiB(bytes / (1024 * 1024 * 1024))
        }
    }

    pub const fn convert(&mut self) {
        match self {
            Self::B(b) if *b > 1024 => *self = Self::KiB(*b / 1024),
            Self::KiB(kib) if *kib > 1024 => *self = Self::MiB(*kib / 1024),
            Self::MiB(mib) if *mib > 1024 => *self = Self::GiB(*mib / 1024),
            _ => (),
        }
    }
}

impl core::fmt::Display for MemoryUnits {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::B(b) => write!(f, "{} B", b),
            Self::KiB(kib) => write!(f, "{} KiB", kib),
            Self::MiB(mib) => write!(f, "{} MiB", mib),
            Self::GiB(gib) => write!(f, "{} GiB", gib),
        }
    }
}
