Initial Commit

This commit is contained in:
Xavier Moffett 2023-09-27 01:12:35 -04:00
commit ccc5f5b01b
7 changed files with 693 additions and 0 deletions

7
Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "simplebyteunit"
version = "0.1.0"

5
Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[package]
name = "simplebyteunit"
authors = ["Xavier R.M."]
version = "0.1.0"
edition = "2021"

31
README.md Normal file
View File

@ -0,0 +1,31 @@
# SimpleByteUnit
SimpleByteUnit is a crate which provides a thin encapsulate for integer primitives to facilitate a fast, simple, yet ergonomic byteunit implementation.
## Usage
Add 'simplebyteunit' to your 'Cargo.toml':
```
[dependencies]
simplebyteunit = "0.1.0"
```
## Example
Generate a human-readable, formatted ByteUnit:
```
use simplebyteunit::simplebyteunit::*;
let byteunit_var = 500000.to_byteunit(SI);
println!("{byteunit_var}");
```
Output:
```
500 KB
```

56
src/input.rs Normal file
View File

@ -0,0 +1,56 @@
/*
* 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 parse_input(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),
string if string.ends_with("gib") => ("gib", G, true),
string if string.ends_with("tib") => ("tib", T, true),
string if string.ends_with("pib") => ("pib", P, true),
string if string.ends_with("eib") => ("eib", E, true),
string if string.ends_with("kb") => ("kb", K, false),
string if string.ends_with("mb") => ("mb", M, false),
string if string.ends_with("gb") => ("gb", G, false),
string if string.ends_with("tb") => ("tb", T, false),
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.")))?
};
let s = s.to_lowercase()
.replace(v.0, "")
.replace(" ", "");
let multiplier = match v.2 { true => 1024.0, false => 1000.0 };
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.")))
}
}
pub fn input_arithmetic(input: (f64, f64, i8, bool)) -> (bool, i64) {
let iec = input.3;
let power_of = input.2;
let multiplier = input.1;
let mut value = input.0;
let mut power: i8 = 0;
while power < power_of {
value = value * multiplier;
power += 1;
}
(iec, value as i64)
}

128
src/lib.rs Normal file
View File

@ -0,0 +1,128 @@
/*
* 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
*/
/*!
A thin encapsulate for integer primitives to facilitate a fast, simple, yet ergonomic byteunit implementation.
# Getting Started
Add 'simplebyteunit' to your 'Cargo.toml':
```toml
[dependencies]
simplebyteunit = "0.1.0"
```
## Example
Generate a human-readable, formatted ByteUnit:
```rust
use simplebyteunit::simplebyteunit::*;
let byteunit_var = 500000.to_byteunit(SI);
println!("{byteunit_var}");
```
Output:
```shell
500 KB
````
## Parsing strings into ByteUnits
And then you can parse formatted strings back into a ByteUnit
```rust
use simplebyteunit::simplebyteunit::*;
let byteunit_var: ByteUnit<i64> = "500 KB".into();
println!("{byteunit_var}");
```
Output:
```shell
500 KB
````
## Simple arithmetic operations
Addition, subtraction, multiplication, subtraction, and division are supported on this type.
```rust
use simplebyteunit::simplebyteunit::*;
let a: ByteUnit<i64> = ByteUnit::SI(5000000);
let b: ByteUnit<i64> = ByteUnit::SI(5000000);
let byteunit_sum = a + b;
println!("{byteunit_sum}");
```
Output:
```shell
1.0 MB
```
## Equal/and/or operations
Equal operations are supported on this type:
```rust
use simplebyteunit::simplebyteunit::*;
let a: ByteUnit<i64> = "500 KiB".into();
let b: ByteUnit<i64> = "500 KiB".into();
let byteunit_bool = a == b;
println!("{byteunit_bool}");
```
Output:
```shell
true
````
Or operations are supported directly on this type:
```rust
use simplebyteunit::simplebyteunit::*;
let a: ByteUnit<i64> = 5000000.to_byteunit(IEC);
let b: ByteUnit<i64> = 5000000.to_byteunit(IEC);
let byteunit_bool = a >= b;
println!("{byteunit_bool}");
```
Output:
```shell
true
```
*/
pub mod simplebyteunit;
mod input;
mod test;

276
src/simplebyteunit.rs Normal file
View File

@ -0,0 +1,276 @@
/*
* 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
*/
/*!
Fast, stupid simple ByteUnit implementation
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};
/// IEC ByteUnit (x*1024)
pub const IEC: ByteUnit<()> = ByteUnit::IEC(());
/// SI ByteUnit (x*1000)
pub const SI: ByteUnit<()> = ByteUnit::SI(());
/// Power of 6 (Exa/Exbi)
pub const E: i8 = 6;
/// Power of 5 (Peta/Pebi)
pub const P: i8 = 5;
/// Power of 4 (Tera/Tebi)
pub const T: i8 = 4;
/// Power of 3 (Giga/Gibi)
pub const G: i8 = 3;
/// Power of 2 (Mega/Mebi)
pub const M: i8 = 2;
/// Power of 1 (Kilo/Kibi)
pub const K: i8 = 1;
/// Base unit
pub const B: i8 = 0;
/// Thin encapsulate of a supported, primitive integer to provide simple byteunit facilities
pub enum ByteUnit<T: Copy> {
IEC(T),
SI(T),
}
#[derive(Debug)]
pub enum Error {
InvalidUnit(String),
ErroroneousInput(String),
}
/// Trait providing generic abstraction to encapsulate primitive in a ByteUnit
pub trait ToByteUnit<T: Copy> {
fn to_byteunit(self, byte: ByteUnit<()>) -> ByteUnit<T>;
}
impl ToByteUnit<u32> for u32 where i64: From<u32> {
fn to_byteunit(self, unit: ByteUnit<()>) -> ByteUnit<u32> {
match unit {
ByteUnit::IEC(()) => ByteUnit::IEC(self),
ByteUnit::SI(()) => ByteUnit::SI(self),
}
}
}
impl ToByteUnit<i32> for i32 where i64: From<i32> {
fn to_byteunit(self, unit: ByteUnit<()>) -> ByteUnit<i32> {
match unit {
ByteUnit::IEC(()) => ByteUnit::IEC(self),
ByteUnit::SI(()) => ByteUnit::SI(self),
}
}
}
impl ToByteUnit<i64> for i64 where i64: From<i64> {
fn to_byteunit(self, unit: ByteUnit<()>) -> ByteUnit<i64> {
match unit {
ByteUnit::IEC(()) => ByteUnit::IEC(self),
ByteUnit::SI(()) => ByteUnit::SI(self),
}
}
}
impl <T>ByteUnit<T> where i64: From<T>, T: Copy {
fn value(&self) -> (T, f64) {
match self {
Self::IEC(val) => (*val, 1024.0),
Self::SI(val) => (*val, 1000.0)
}
}
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)),
}
}
/// Acquire and return base value of encapsulated primitive
pub fn val(&self) -> T {
match self {
Self::IEC(val) => *val,
Self::SI(val) => *val,
}
}
/// 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))
}
/// Returns a formatted string with a maximum power of 1 (Kilo/Kibi)
pub fn k(&self) -> String {
self.format(self.arithmetic(K))
}
/// Returns a formatted string with a maximum power of 2 (Mega/Mebi)
pub fn m(&self) -> String {
self.format(self.arithmetic(M))
}
/// Returns a formatted string with a maximum power of 3 (Giga/Gibi)
pub fn g(&self) -> String {
self.format(self.arithmetic(G))
}
/// Returns a formatted string with a maximum power of 4 (Tera/Tebi)
pub fn p(&self) -> String {
self.format(self.arithmetic(P))
}
/// Returns a formatted string with a maximum power of 5 (Peta/Pebi)
pub fn t(&self) -> String {
self.format(self.arithmetic(T))
}
/// Returns a formatted string with a maximum power of 6 (Exa/Exbi)
pub fn e(&self) -> String {
self.format(self.arithmetic(E))
}
}
/// Display implementation with a maximum power of 6 (Exa/Exbi)
impl<T> Display for ByteUnit<T> where i64: From<T>, T: Copy {
fn fmt(&self, f:&mut Formatter<'_>) -> std::fmt::Result {
let arithmetic = self.arithmetic(E);
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)),
}
}
}
impl From<&str> for ByteUnit<i64> where i64: From<i64>, i64: Copy {
fn from(value: &str) -> Self {
ByteUnit::from_str(value).unwrap()
}
}
impl FromStr for ByteUnit<i64> where i64: From<i64>, i64: Copy {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let input = input_arithmetic(parse_input(s)?);
match input.0 {
true => Ok(ByteUnit::IEC(input.1)), false => Ok(ByteUnit::SI(input.1))
}
}
}
impl <T>PartialOrd for ByteUnit<T> where i64: From<T>, T: Copy + PartialEq {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
let value = i64::from(self.val());
let other = i64::from(other.val());
value.partial_cmp(&other)
}
}
impl <T>PartialEq for ByteUnit<T> where i64: From<T>, T: Copy + PartialEq {
fn eq(&self, other: &Self) -> bool {
other.val().eq(&self.val())
}
}
impl <T>Add for ByteUnit<T> where i64: From<T>, T: Copy + Add<Output = T> {
type Output = Self;
fn add(self, input: Self) -> Self::Output {
match self {
Self::IEC(value) => Self::IEC(value + input.val()),
Self::SI(value) => Self::SI(value + input.val()),
}
}
}
impl <T>Sub for ByteUnit<T> where i64: From<T>, T: Copy + Sub<Output = T> {
type Output = Self;
fn sub(self, input: Self) -> Self::Output {
match self {
Self::IEC(value) => Self::IEC(value - input.val()),
Self::SI(value) => Self::SI(value - input.val()),
}
}
}
impl <T>Mul for ByteUnit<T> where i64: From<T>, T: Copy + Mul<Output = T> {
type Output = Self;
fn mul(self, input: Self) -> Self::Output {
match self {
Self::IEC(value) => Self::IEC(value * input.val()),
Self::SI(value) => Self::SI(value * input.val()),
}
}
}
impl <T>Div for ByteUnit<T> where i64: From<T>, T: Copy + Div<Output = T> {
type Output = Self;
fn div(self, input: Self) -> Self::Output {
match self {
Self::IEC(value) => Self::IEC(value / input.val()),
Self::SI(value) => Self::SI(value / input.val()),
}
}
}

190
src/test.rs Normal file
View File

@ -0,0 +1,190 @@
/*
* 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
*/
#![allow(dead_code,unused_imports)]
use super::simplebyteunit::*;
const POSITIVE_5B: i64 = 5000;
const NEGATIVE_5B: i64 = -5000;
const POSITIVE_5K: i64 = 5000000;
const NEGATIVE_5K: i64 = -5000000;
const POSITIVE_5G: i64 = 5000000000;
const NEGATIVE_5G: i64 = -5000000000;
const POSITIVE_5T: i64 = 5000000000000;
const NEGATIVE_5T: i64 = -5000000000000;
const POSITIVE_5P: i64 = 5000000000000000;
const NEGATIVE_5P: i64 = -5000000000000000;
const POSITIVE_5E: i64 = 5000000000000000000;
const NEGATIVE_5E: i64 = -5000000000000000000;
#[test]
fn format_all() {
assert_eq!(NEGATIVE_5E.to_byteunit(SI).to_string(), "-5.00 EB");
assert_eq!(POSITIVE_5E.to_byteunit(SI).to_string(), "5.00 EB");
assert_eq!(NEGATIVE_5P.to_byteunit(SI).to_string(), "-5.00 PB");
assert_eq!(POSITIVE_5P.to_byteunit(SI).to_string(), "5.00 PB");
assert_eq!(NEGATIVE_5T.to_byteunit(SI).to_string(), "-5.00 TB");
assert_eq!(POSITIVE_5T.to_byteunit(SI).to_string(), "5.00 TB");
assert_eq!(NEGATIVE_5G.to_byteunit(SI).to_string(), "-5.00 GB");
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!(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");
assert_eq!(POSITIVE_5P.to_byteunit(IEC).to_string(), "4.44 PiB");
assert_eq!(NEGATIVE_5T.to_byteunit(IEC).to_string(), "-4.55 TiB");
assert_eq!(NEGATIVE_5T.to_byteunit(IEC).to_string(), "-4.55 TiB");
assert_eq!(POSITIVE_5T.to_byteunit(IEC).to_string(), "4.55 TiB");
assert_eq!(NEGATIVE_5G.to_byteunit(IEC).to_string(), "-4.66 GiB");
assert_eq!(POSITIVE_5G.to_byteunit(IEC).to_string(), "4.66 GiB");
assert_eq!(POSITIVE_5K.to_byteunit(IEC).to_string(), "4.77 MiB");
assert_eq!(NEGATIVE_5K.to_byteunit(IEC).to_string(), "-4.77 MiB");
assert_eq!(POSITIVE_5B.to_byteunit(IEC).to_string(), "4.88 KiB");
assert_eq!(NEGATIVE_5B.to_byteunit(IEC).to_string(), "-4.88 KiB");
}
#[test]
fn bytes() {
assert_eq!(NEGATIVE_5B.to_byteunit(IEC).pow(B), "-5000 B");
assert_eq!(NEGATIVE_5B.to_byteunit(SI).pow(B), "-5000 B");
assert_eq!(POSITIVE_5B.to_byteunit(IEC).pow(B), "5000 B");
assert_eq!(POSITIVE_5B.to_byteunit(SI).pow(B), "5000 B");
assert_eq!(NEGATIVE_5E.to_byteunit(IEC).pow(B), "-5000000000000000000 B");
assert_eq!(NEGATIVE_5E.to_byteunit(SI).pow(B), "-5000000000000000000 B");
assert_eq!(POSITIVE_5E.to_byteunit(IEC).pow(B), "5000000000000000000 B");
assert_eq!(POSITIVE_5E.to_byteunit(SI).pow(B), "5000000000000000000 B");
}
#[test]
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");
}
#[test]
fn m() {
assert_eq!(NEGATIVE_5G.to_byteunit(IEC).pow(M), "-4768.37 MiB");
assert_eq!(POSITIVE_5G.to_byteunit(IEC).pow(M), "4768.37 MiB");
assert_eq!(NEGATIVE_5G.to_byteunit(SI).pow(M), "-5000.00 MB");
assert_eq!(POSITIVE_5G.to_byteunit(SI).pow(M), "5000.00 MB");
}
#[test]
fn g() {
assert_eq!(NEGATIVE_5T.to_byteunit(IEC).pow(G), "-4656.61 GiB");
assert_eq!(POSITIVE_5T.to_byteunit(IEC).pow(G), "4656.61 GiB");
assert_eq!(NEGATIVE_5T.to_byteunit(SI).pow(G), "-5000.00 GB");
assert_eq!(POSITIVE_5T.to_byteunit(SI).pow(G), "5000.00 GB");
}
#[test]
fn t() {
assert_eq!(NEGATIVE_5P.to_byteunit(IEC).pow(T), "-4547.47 TiB");
assert_eq!(POSITIVE_5P.to_byteunit(IEC).pow(T), "4547.47 TiB");
assert_eq!(NEGATIVE_5P.to_byteunit(SI).pow(T), "-5000.00 TB");
assert_eq!(POSITIVE_5P.to_byteunit(SI).pow(T), "5000.00 TB");
}
#[test]
fn p() {
assert_eq!(NEGATIVE_5E.to_byteunit(IEC).pow(P), "-4440.89 PiB");
assert_eq!(POSITIVE_5E.to_byteunit(IEC).pow(P), "4440.89 PiB");
assert_eq!(NEGATIVE_5E.to_byteunit(SI).pow(P), "-5000.00 PB");
assert_eq!(POSITIVE_5E.to_byteunit(SI).pow(P), "5000.00 PB");
}
#[test]
fn e() {
assert_eq!(NEGATIVE_5E.to_byteunit(IEC).pow(E), "-4.34 EiB");
assert_eq!(POSITIVE_5E.to_byteunit(IEC).pow(E), "4.34 EiB");
assert_eq!(NEGATIVE_5E.to_byteunit(SI).pow(E), "-5.00 EB");
assert_eq!(POSITIVE_5E.to_byteunit(SI).pow(E), "5.00 EB");
}
#[test]
fn eq() {
assert_eq!(POSITIVE_5G.to_byteunit(SI) == POSITIVE_5G.to_byteunit(SI), true);
assert_eq!(POSITIVE_5B.to_byteunit(SI) == POSITIVE_5G.to_byteunit(SI), false);
}
#[test]
fn mul() {
let a = ByteUnit::SI(POSITIVE_5B);
let b = ByteUnit::SI(POSITIVE_5K);
let multiplication = a * b;
assert_eq!(multiplication.to_string(), "25.00 GB");
}
#[test]
fn div() {
let a = ByteUnit::SI(POSITIVE_5G);
let b = ByteUnit::SI(POSITIVE_5B);
let division = a / b;
assert_eq!(division.to_string(), "1.00 MB");
}
#[test]
fn add() {
let a = ByteUnit::SI(POSITIVE_5B);
let b = ByteUnit::SI(POSITIVE_5B);
let division = a + b;
assert_eq!(division.to_string(), "10.00 KB");
}
#[test]
fn sub() {
let a = ByteUnit::SI(POSITIVE_5G);
let b = ByteUnit::SI(POSITIVE_5G);
let division = a - b;
assert_eq!(division.to_string(), "0 B");
}
#[test]
fn partial_cmp() {
let a = ByteUnit::SI(NEGATIVE_5G);
let b = ByteUnit::SI(POSITIVE_5G);
assert_eq!(a > b, false);
assert_eq!(a < b, true);
assert_eq!(a >= a, true);
assert_eq!(b <= b, true);
}
#[test]
fn partial_eq() {
let a = ByteUnit::SI(POSITIVE_5G);
let b = ByteUnit::SI(POSITIVE_5G);
assert_eq!(a == b, true);
assert_eq!(a != b, false);
}
#[test]
fn into() {
let a: ByteUnit<i64> = "-4.34 EiB".into();
let b: ByteUnit<i64> = "-2.50 GB".into();
assert_eq!(a.to_string(), "-4.34 EiB");
assert_eq!(b.to_string(), "-2.50 GB");
}