From 7d182d860b1548c4f97227376e169ca932e76d75 Mon Sep 17 00:00:00 2001 From: Xavier Date: Thu, 28 Sep 2023 23:08:41 -0400 Subject: [PATCH] Fix to a minor fumble over SI prefixes, some internal refactoring, and minor API additions. - Added new function max() to byteunit type. - New public constant 'simplebyteunit::MAX' defining the maximum supported power. - Output module to house arithmetic and prefix functions for output values. - For the sake of SI compliance, kilobytes are now prefixed as 'kB'. --- src/input.rs | 8 ++--- src/lib.rs | 11 ++++--- src/output.rs | 51 +++++++++++++++++++++++++++++ src/simplebyteunit.rs | 74 ++++++++++++------------------------------- src/test.rs | 10 +++--- 5 files changed, 87 insertions(+), 67 deletions(-) create mode 100644 src/output.rs diff --git a/src/input.rs b/src/input.rs index 4be6772..0ab447f 100644 --- a/src/input.rs +++ b/src/input.rs @@ -12,7 +12,7 @@ use crate::simplebyteunit::*; -pub fn parse_input(s: &str) -> Result<(f64, f64, i8, bool), Error> { +pub fn parse(s: &str) -> Result<(f64, f64, i8, bool), Error> { let v = match s.to_lowercase() { string if string.ends_with("kib") => ("kib", K, true), string if string.ends_with("mib") => ("mib", M, true), @@ -27,7 +27,7 @@ pub fn parse_input(s: &str) -> Result<(f64, f64, i8, bool), Error> { string if string.ends_with("pb") => ("pb", P, false), string if string.ends_with("eb") => ("eb", E, false), string if string.ends_with("b") => ("b", B, false), - _ => Err(Error::InvalidUnit(format!("{s} contains no supported nor valid byteunits.")))? + _ => Err(Error::InvalidUnit(format!("'{s}' contains no supported nor valid byteunits.")))? }; let s = s.to_lowercase() .replace(v.0, "") @@ -36,11 +36,11 @@ pub fn parse_input(s: &str) -> Result<(f64, f64, i8, bool), Error> { match s.parse() { Ok(val) => Ok((val, multiplier, v.1, v.2)), - Err(_) => Err(Error::ErroroneousInput(format!("{s} contains an invalid float or integer value."))) + Err(_) => Err(Error::ErroroneousInput(format!("'{s}' contains an invalid float or integer value."))) } } -pub fn input_arithmetic(input: (f64, f64, i8, bool)) -> (bool, i64) { +pub fn arithmetic(input: (f64, f64, i8, bool)) -> (bool, i64) { let iec = input.3; let power_of = input.2; let multiplier = input.1; diff --git a/src/lib.rs b/src/lib.rs index feb2aeb..bfc6bb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ Add 'simplebyteunit' to your 'Cargo.toml': ```toml [dependencies] -simplebyteunit = "0.1.0" +simplebyteunit = "0.2.0" ``` ## Example @@ -39,7 +39,7 @@ println!("{byteunit_var}"); Output: ```shell -500 KB +500 kB ```` ## Parsing strings into ByteUnits @@ -49,7 +49,7 @@ And then you can parse formatted strings back into a ByteUnit ```rust use simplebyteunit::simplebyteunit::*; -let byteunit_var: ByteUnit = "500 KB".into(); +let byteunit_var: ByteUnit = "500 kB".into(); println!("{byteunit_var}"); @@ -58,7 +58,7 @@ println!("{byteunit_var}"); Output: ```shell -500 KB +500 kB ```` ## Simple arithmetic operations @@ -102,7 +102,7 @@ Output: true ```` -Or operations are supported directly on this type: +Or operations are also supported on this type: ```rust use simplebyteunit::simplebyteunit::*; @@ -125,4 +125,5 @@ true pub mod simplebyteunit; mod input; +mod output; mod test; diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..819f889 --- /dev/null +++ b/src/output.rs @@ -0,0 +1,51 @@ +/* + * SimpleByteUnit + * + * Copyright 2023 Xaver R.M. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +use crate::simplebyteunit::*; + +pub fn prefix<'a, T: Copy>(unit: &ByteUnit, i: i8) -> &'a str { + match unit { + ByteUnit::IEC(_) => match i { + K => "KiB", + M => "MiB", + G => "GiB", + T => "TiB", + P => "PiB", + E => "EiB", + _ => "B" + }, + ByteUnit::SI(_) => match i { + K => "kB", + M => "MB", + G => "GB", + T => "TB", + P => "PB", + E => "EB", + _ => "B" + } + } +} + +pub fn arithmetic(value: (T, f64), power_of: i8) -> (i8, f64) where i64: From { + let bytes: i64 = value.0.into(); + let diviser = value.1; + let positive = bytes > -1; + let mut bytes: f64 = if positive { bytes } else { -bytes } as f64; + let mut power = 0; + + while bytes >= diviser && power < power_of { + bytes = bytes / diviser; + power += 1; + } + + match positive { true => (power, bytes), false => (power, -bytes) } +} diff --git a/src/simplebyteunit.rs b/src/simplebyteunit.rs index 788c134..d23241c 100644 --- a/src/simplebyteunit.rs +++ b/src/simplebyteunit.rs @@ -18,7 +18,7 @@ Provides a simple way to encapsulate primitives as byteunits. use std::{fmt::{Display, Formatter}, ops::{Mul, Div, Add, Sub}, str::FromStr}; -use crate::input::{parse_input, input_arithmetic}; +use crate::{input, output}; /// IEC ByteUnit (x*1024) pub const IEC: ByteUnit<()> = ByteUnit::IEC(()); @@ -38,6 +38,8 @@ pub const M: i8 = 2; pub const K: i8 = 1; /// Base unit pub const B: i8 = 0; +/// Maximum supported power +pub const MAX: i8 = E; /// Thin encapsulate of a supported, primitive integer to provide simple byteunit facilities pub enum ByteUnit { @@ -91,52 +93,13 @@ impl ByteUnit where i64: From, T: Copy { } } - fn unit_suffix<'a>(&self, i: i8) -> &'a str { - match self { - Self::IEC(_) => match i { - K => "KiB", - M => "MiB", - G => "GiB", - T => "TiB", - P => "PiB", - E => "EiB", - _ => "B" - }, - Self::SI(_) => match i { - K => "KB", - M => "MB", - G => "GB", - T => "TB", - P => "PB", - E => "EB", - _ => "B" - } - } - } - - fn arithmetic(&self, power_of: i8) -> (i8, f64) { - let value: (T, f64) = self.value(); - let bytes: i64 = value.0.into(); - let diviser = value.1; - let positive = bytes > -1; - let mut bytes: f64 = if positive { bytes } else { -bytes } as f64; - let mut power = 0; - - while bytes >= diviser && power < power_of { - bytes = bytes / diviser; - power += 1; - } - - match positive { true => (power, bytes), false => (power, -bytes) } - } - fn format(&self, arithmetic: (i8, f64)) -> String { let power = arithmetic.0; let value = arithmetic.1; match power { - B => format!("{:.0} {}", value, self.unit_suffix(power)), - _ => format!("{:.2} {}", value, self.unit_suffix(power)), + B => format!("{:.0} {}", value, output::prefix(self, power)), + _ => format!("{:.2} {}", value, output::prefix(self, power)), } } @@ -148,52 +111,57 @@ impl ByteUnit where i64: From, T: Copy { } } + /// Returns a formatted string with up-to a maximum supported power. + pub fn max(&self) -> String { + self.format(output::arithmetic(self.value(), MAX)) + } + /// Returns a formatted string with a maximum of the specified power. pub fn pow(&self, power_of: i8) -> String { - self.format(self.arithmetic(power_of)) + self.format(output::arithmetic(self.value(), power_of)) } /// Returns a formatted string with a maximum power of 1 (Kilo/Kibi) pub fn k(&self) -> String { - self.format(self.arithmetic(K)) + self.format(output::arithmetic(self.value(), K)) } /// Returns a formatted string with a maximum power of 2 (Mega/Mebi) pub fn m(&self) -> String { - self.format(self.arithmetic(M)) + self.format(output::arithmetic(self.value(), M)) } /// Returns a formatted string with a maximum power of 3 (Giga/Gibi) pub fn g(&self) -> String { - self.format(self.arithmetic(G)) + self.format(output::arithmetic(self.value(), G)) } /// Returns a formatted string with a maximum power of 4 (Tera/Tebi) pub fn p(&self) -> String { - self.format(self.arithmetic(P)) + self.format(output::arithmetic(self.value(), P)) } /// Returns a formatted string with a maximum power of 5 (Peta/Pebi) pub fn t(&self) -> String { - self.format(self.arithmetic(T)) + self.format(output::arithmetic(self.value(), T)) } /// Returns a formatted string with a maximum power of 6 (Exa/Exbi) pub fn e(&self) -> String { - self.format(self.arithmetic(E)) + self.format(output::arithmetic(self.value(), E)) } } /// Display implementation with a maximum power of 6 (Exa/Exbi) impl Display for ByteUnit where i64: From, T: Copy { fn fmt(&self, f:&mut Formatter<'_>) -> std::fmt::Result { - let arithmetic = self.arithmetic(E); + let arithmetic = output::arithmetic(self.value(), MAX); let bytes = arithmetic.1; let index = arithmetic.0; match index { - B => write!(f, "{:.0} {}", bytes, self.unit_suffix(index)), - _ => write!(f, "{:.2} {}", bytes, self.unit_suffix(index)), + B => write!(f, "{:.0} {}", bytes, output::prefix(self, index)), + _ => write!(f, "{:.2} {}", bytes, output::prefix(self, index)), } } } @@ -208,7 +176,7 @@ impl FromStr for ByteUnit where i64: From, i64: Copy { type Err = Error; fn from_str(s: &str) -> Result { - let input = input_arithmetic(parse_input(s)?); + let input = input::arithmetic(input::parse(s)?); match input.0 { true => Ok(ByteUnit::IEC(input.1)), false => Ok(ByteUnit::SI(input.1)) diff --git a/src/test.rs b/src/test.rs index d4dd515..ae7bebf 100644 --- a/src/test.rs +++ b/src/test.rs @@ -39,8 +39,8 @@ fn format_all() { assert_eq!(POSITIVE_5G.to_byteunit(SI).to_string(), "5.00 GB"); assert_eq!(POSITIVE_5K.to_byteunit(SI).to_string(), "5.00 MB"); assert_eq!(NEGATIVE_5K.to_byteunit(SI).to_string(), "-5.00 MB"); - assert_eq!(POSITIVE_5B.to_byteunit(SI).to_string(), "5.00 KB"); - assert_eq!(NEGATIVE_5B.to_byteunit(SI).to_string(), "-5.00 KB"); + assert_eq!(POSITIVE_5B.to_byteunit(SI).to_string(), "5.00 kB"); + assert_eq!(NEGATIVE_5B.to_byteunit(SI).to_string(), "-5.00 kB"); assert_eq!(NEGATIVE_5E.to_byteunit(IEC).to_string(), "-4.34 EiB"); assert_eq!(POSITIVE_5E.to_byteunit(IEC).to_string(), "4.34 EiB"); assert_eq!(NEGATIVE_5P.to_byteunit(IEC).to_string(), "-4.44 PiB"); @@ -73,8 +73,8 @@ fn bytes() { fn k() { assert_eq!(NEGATIVE_5K.to_byteunit(IEC).pow(K), "-4882.81 KiB"); assert_eq!(POSITIVE_5K.to_byteunit(IEC).pow(K), "4882.81 KiB"); - assert_eq!(NEGATIVE_5K.to_byteunit(SI).pow(K), "-5000.00 KB"); - assert_eq!(POSITIVE_5K.to_byteunit(SI).pow(K), "5000.00 KB"); + assert_eq!(NEGATIVE_5K.to_byteunit(SI).pow(K), "-5000.00 kB"); + assert_eq!(POSITIVE_5K.to_byteunit(SI).pow(K), "5000.00 kB"); } #[test] @@ -147,7 +147,7 @@ fn add() { let b = ByteUnit::SI(POSITIVE_5B); let division = a + b; - assert_eq!(division.to_string(), "10.00 KB"); + assert_eq!(division.to_string(), "10.00 kB"); } #[test]