extern crate utf8_width;
use core::convert::TryFrom;
use core::str::{from_utf8_unchecked, Bytes, FromStr};
#[cfg(feature = "serde")]
use alloc::string::String;
use alloc::fmt::{self, Display, Formatter};
use crate::UnitIncorrectError;
#[cfg(feature = "serde")]
use crate::serde::ser::{Serialize, Serializer};
#[cfg(feature = "serde")]
use crate::serde::de::{Deserialize, Deserializer, Error as DeError, Visitor};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ByteUnit {
B,
KB,
KiB,
MB,
MiB,
GB,
GiB,
TB,
TiB,
PB,
PiB,
#[cfg(feature = "u128")]
EB,
#[cfg(feature = "u128")]
EiB,
#[cfg(feature = "u128")]
ZB,
#[cfg(feature = "u128")]
ZiB,
}
impl ByteUnit {
#[allow(clippy::should_implement_trait)]
pub fn from_str<S: AsRef<str>>(unit: S) -> Result<ByteUnit, UnitIncorrectError> {
let s = unit.as_ref().trim();
let mut bytes = s.bytes();
read_xib(bytes.next(), bytes)
}
#[inline]
pub fn as_str(self) -> &'static str {
match self {
ByteUnit::B => "B",
ByteUnit::KB => "KB",
ByteUnit::KiB => "KiB",
ByteUnit::MB => "MB",
ByteUnit::MiB => "MiB",
ByteUnit::GB => "GB",
ByteUnit::GiB => "GiB",
ByteUnit::TB => "TB",
ByteUnit::TiB => "TiB",
ByteUnit::PB => "PB",
ByteUnit::PiB => "PiB",
#[cfg(feature = "u128")]
ByteUnit::EB => "EB",
#[cfg(feature = "u128")]
ByteUnit::EiB => "EiB",
#[cfg(feature = "u128")]
ByteUnit::ZB => "ZB",
#[cfg(feature = "u128")]
ByteUnit::ZiB => "ZiB",
}
}
#[cfg(feature = "u128")]
#[inline]
pub fn get_unit_bytes(self) -> u128 {
match self {
ByteUnit::B => 1,
ByteUnit::KB => n_kb_bytes!(),
ByteUnit::KiB => n_kib_bytes!(),
ByteUnit::MB => n_mb_bytes!(),
ByteUnit::MiB => n_mib_bytes!(),
ByteUnit::GB => n_gb_bytes!(),
ByteUnit::GiB => n_gib_bytes!(),
ByteUnit::TB => n_tb_bytes!(),
ByteUnit::TiB => n_tib_bytes!(),
ByteUnit::PB => n_pb_bytes!(),
ByteUnit::PiB => n_pib_bytes!(),
ByteUnit::EB => n_eb_bytes!(),
ByteUnit::EiB => n_eib_bytes!(),
ByteUnit::ZB => n_zb_bytes!(),
ByteUnit::ZiB => n_zib_bytes!(),
}
}
#[cfg(not(feature = "u128"))]
#[inline]
pub fn get_unit_bytes(self) -> u64 {
match self {
ByteUnit::B => 1,
ByteUnit::KB => n_kb_bytes!(),
ByteUnit::KiB => n_kib_bytes!(),
ByteUnit::MB => n_mb_bytes!(),
ByteUnit::MiB => n_mib_bytes!(),
ByteUnit::GB => n_gb_bytes!(),
ByteUnit::GiB => n_gib_bytes!(),
ByteUnit::TB => n_tb_bytes!(),
ByteUnit::TiB => n_tib_bytes!(),
ByteUnit::PB => n_pb_bytes!(),
ByteUnit::PiB => n_pib_bytes!(),
}
}
}
impl Display for ByteUnit {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ByteUnit {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl TryFrom<&str> for ByteUnit {
type Error = UnitIncorrectError;
#[inline]
fn try_from(s: &str) -> Result<Self, Self::Error> {
ByteUnit::from_str(s)
}
}
impl FromStr for ByteUnit {
type Err = UnitIncorrectError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
ByteUnit::from_str(s)
}
}
#[inline]
pub(crate) fn get_char_from_bytes(e: u8, mut bytes: Bytes) -> char {
let width = unsafe { utf8_width::get_width_assume_valid(e) };
let mut char_bytes = [e; 4];
if width > 1 {
for e in char_bytes[1..].iter_mut().take(width - 1) {
*e = bytes.next().unwrap();
}
}
let char_str = unsafe { from_utf8_unchecked(&char_bytes[..width]) };
char::from_str(char_str).unwrap()
}
pub(crate) fn read_xib(e: Option<u8>, mut bytes: Bytes) -> Result<ByteUnit, UnitIncorrectError> {
match e {
Some(e) => {
match e.to_ascii_uppercase() {
b'B' => {
match bytes.next() {
Some(e) => {
Err(UnitIncorrectError {
character: get_char_from_bytes(e, bytes),
expected_characters: &[],
also_expect_no_character: false,
})
}
None => Ok(ByteUnit::B),
}
}
b'K' => {
if read_ib(bytes)? {
Ok(ByteUnit::KiB)
} else {
Ok(ByteUnit::KB)
}
}
b'M' => {
if read_ib(bytes)? {
Ok(ByteUnit::MiB)
} else {
Ok(ByteUnit::MB)
}
}
b'G' => {
if read_ib(bytes)? {
Ok(ByteUnit::GiB)
} else {
Ok(ByteUnit::GB)
}
}
b'T' => {
if read_ib(bytes)? {
Ok(ByteUnit::TiB)
} else {
Ok(ByteUnit::TB)
}
}
b'P' => {
if read_ib(bytes)? {
Ok(ByteUnit::PiB)
} else {
Ok(ByteUnit::PB)
}
}
#[cfg(feature = "u128")]
b'E' => {
if read_ib(bytes)? {
Ok(ByteUnit::EiB)
} else {
Ok(ByteUnit::EB)
}
}
#[cfg(feature = "u128")]
b'Z' => {
if read_ib(bytes)? {
Ok(ByteUnit::ZiB)
} else {
Ok(ByteUnit::ZB)
}
}
_ => {
#[cfg(feature = "u128")]
{
Err(UnitIncorrectError {
character: get_char_from_bytes(e, bytes),
expected_characters: &['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z'],
also_expect_no_character: true,
})
}
#[cfg(not(feature = "u128"))]
{
Err(UnitIncorrectError {
character: get_char_from_bytes(e, bytes),
expected_characters: &['B', 'K', 'M', 'G', 'T', 'P'],
also_expect_no_character: true,
})
}
}
}
}
None => Ok(ByteUnit::B),
}
}
fn read_ib(mut bytes: Bytes) -> Result<bool, UnitIncorrectError> {
match bytes.next() {
Some(e) => {
match e.to_ascii_uppercase() {
b'I' => {
match bytes.next() {
Some(e) => {
match e.to_ascii_uppercase() {
b'B' => Ok(true),
_ => {
Err(UnitIncorrectError {
character: get_char_from_bytes(e, bytes),
expected_characters: &['B'],
also_expect_no_character: false,
})
}
}
}
None => Ok(true),
}
}
b'B' => {
match bytes.next() {
Some(e) => {
Err(UnitIncorrectError {
character: get_char_from_bytes(e, bytes),
expected_characters: &[],
also_expect_no_character: false,
})
}
None => Ok(false),
}
}
_ => {
Err(UnitIncorrectError {
character: get_char_from_bytes(e, bytes),
expected_characters: &['B', 'i'],
also_expect_no_character: false,
})
}
}
}
None => Ok(false),
}
}
#[cfg(feature = "serde")]
impl Serialize for ByteUnit {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer, {
serializer.serialize_str(self.as_str())
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for ByteUnit {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>, {
struct ByteUnitVisitor;
impl<'de> Visitor<'de> for ByteUnitVisitor {
type Value = ByteUnit;
#[inline]
fn expecting(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_str("a byte unit such as \"B\", \"KB\" or \"MiB\"")
}
#[inline]
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: DeError, {
ByteUnit::from_str(v).map_err(DeError::custom)
}
#[inline]
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: DeError, {
ByteUnit::from_str(v.as_str()).map_err(DeError::custom)
}
}
deserializer.deserialize_any(ByteUnitVisitor)
}
}