simplebyteunit/
simplebyteunit.rs

1/*
2 * SimpleByteUnit
3 *
4 * Copyright (C) 2023-2025 Xavier Moffett <sapphirus@azorium.net>
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *	http://www.apache.org/licenses/LICENSE-2.0
12 */
13
14/*!
15Fast, stupid simple ByteUnit implementation
16
17Provides a simple way to encapsulate primitives as byteunits.
18*/
19use std::{
20    fmt::{Debug, Display, Formatter},
21    ops::{Add, Div, Mul, Sub},
22    str::FromStr,
23};
24
25use crate::{
26    arithmetic::{divisor, multiplier},
27    suffix::{parse, suffix},
28};
29
30/// IEC ByteUnit (x*1024)
31pub const IEC: ByteUnit<()> = ByteUnit::IEC(());
32/// SI ByteUnit (x*1000)
33pub const SI: ByteUnit<()> = ByteUnit::SI(());
34/// Power of 6 (Exa/Exbi)
35pub const E: i8 = 6;
36/// Power of 5 (Peta/Pebi)
37pub const P: i8 = 5;
38/// Power of 4 (Tera/Tebi)
39pub const T: i8 = 4;
40/// Power of 3 (Giga/Gibi)
41pub const G: i8 = 3;
42/// Power of 2 (Mega/Mebi)
43pub const M: i8 = 2;
44/// Power of 1 (Kilo/Kibi)
45pub const K: i8 = 1;
46/// Base unit
47pub const B: i8 = 0;
48/// Maximum supported power
49pub const MAX: i8 = E;
50
51/// Thin encapsulate of a supported, primitive integer to provide simple byteunit facilities
52pub enum ByteUnit<T: Copy> {
53    IEC(T),
54    SI(T),
55}
56
57#[derive(Debug)]
58pub enum Error {
59    InvalidUnit(String),
60    ErroroneousInput(String),
61}
62
63/// Trait providing generic abstraction to encapsulate primitive in a ByteUnit
64pub trait ToByteUnit<T: Copy> {
65    fn to_byteunit(self, byte: ByteUnit<()>) -> ByteUnit<T>;
66}
67
68impl ToByteUnit<u32> for u32
69where
70    i64: From<u32>,
71{
72    fn to_byteunit(self, unit: ByteUnit<()>) -> ByteUnit<u32> {
73        match unit {
74            ByteUnit::IEC(()) => ByteUnit::IEC(self),
75            ByteUnit::SI(()) => ByteUnit::SI(self),
76        }
77    }
78}
79
80impl ToByteUnit<i32> for i32
81where
82    i64: From<i32>,
83{
84    fn to_byteunit(self, unit: ByteUnit<()>) -> ByteUnit<i32> {
85        match unit {
86            ByteUnit::IEC(()) => ByteUnit::IEC(self),
87            ByteUnit::SI(()) => ByteUnit::SI(self),
88        }
89    }
90}
91
92impl ToByteUnit<i64> for i64
93where
94    i64: From<i64>,
95{
96    fn to_byteunit(self, unit: ByteUnit<()>) -> ByteUnit<i64> {
97        match unit {
98            ByteUnit::IEC(()) => ByteUnit::IEC(self),
99            ByteUnit::SI(()) => ByteUnit::SI(self),
100        }
101    }
102}
103
104impl<T> ByteUnit<T>
105where
106    i64: From<T>,
107    T: Copy,
108{
109    fn value(&self) -> (T, f64) {
110        match self {
111            Self::IEC(val) => (*val, 1024.0),
112            Self::SI(val) => (*val, 1000.0),
113        }
114    }
115
116    fn format(&self, arithmetic: (i8, f64)) -> String {
117        let power = arithmetic.0;
118        let value = arithmetic.1;
119
120        match power {
121            B => format!("{:.0} {}", value, suffix(self, power)),
122            _ => format!("{:.2} {}", value, suffix(self, power)),
123        }
124    }
125
126    /// Acquire and return base value of encapsulated primitive
127    pub fn val(&self) -> T {
128        match self {
129            Self::IEC(val) => *val,
130            Self::SI(val) => *val,
131        }
132    }
133
134    /// Returns a formatted string with up-to a maximum supported power.
135    pub fn max(&self) -> String {
136        self.format(divisor(self.value(), MAX))
137    }
138
139    /// Returns a formatted string with a maximum of the specified power.
140    pub fn pow(&self, power_of: i8) -> String {
141        self.format(divisor(self.value(), power_of))
142    }
143
144    /// Returns a formatted string with a maximum power of 1 (Kilo/Kibi)
145    pub fn k(&self) -> String {
146        self.format(divisor(self.value(), K))
147    }
148
149    /// Returns a formatted string with a maximum power of 2 (Mega/Mebi)
150    pub fn m(&self) -> String {
151        self.format(divisor(self.value(), M))
152    }
153
154    /// Returns a formatted string with a maximum power of 3 (Giga/Gibi)
155    pub fn g(&self) -> String {
156        self.format(divisor(self.value(), G))
157    }
158
159    /// Returns a formatted string with a maximum power of 4 (Tera/Tebi)
160    pub fn p(&self) -> String {
161        self.format(divisor(self.value(), P))
162    }
163
164    /// Returns a formatted string with a maximum power of 5 (Peta/Pebi)
165    pub fn t(&self) -> String {
166        self.format(divisor(self.value(), T))
167    }
168
169    /// Returns a formatted string with a maximum power of 6 (Exa/Exbi)
170    pub fn e(&self) -> String {
171        self.format(divisor(self.value(), E))
172    }
173}
174
175/// Display implementation with a maximum power of 6 (Exa/Exbi)
176impl<T> Display for ByteUnit<T>
177where
178    i64: From<T>,
179    T: Copy,
180{
181    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
182        let arithmetic = divisor(self.value(), MAX);
183        let bytes = arithmetic.1;
184        let index = arithmetic.0;
185
186        match index {
187            B => write!(f, "{:.0} {}", bytes, suffix(self, index)),
188            _ => write!(f, "{:.2} {}", bytes, suffix(self, index)),
189        }
190    }
191}
192
193/// Debug implementation with a maximum power of 6 (Exa/Exbi)
194impl<T> Debug for ByteUnit<T>
195where
196    i64: From<T>,
197    T: Copy,
198{
199    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
200        let arithmetic = divisor(self.value(), MAX);
201        let bytes = arithmetic.1;
202        let index = arithmetic.0;
203
204        match index {
205            B => write!(f, "'{:.0} {}'", bytes, suffix(self, index)),
206            _ => write!(f, "'{:.2} {}'", bytes, suffix(self, index)),
207        }
208    }
209}
210
211impl<T> From<&str> for ByteUnit<T>
212where
213    i64: From<T>,
214    T: Copy,
215    T: From<i64>,
216{
217    fn from(value: &str) -> Self {
218        ByteUnit::from_str(value).unwrap()
219    }
220}
221
222impl<T> FromStr for ByteUnit<T>
223where
224    i64: From<T>,
225    T: Copy,
226    i64: From<T>,
227    T: From<i64>,
228{
229    type Err = Error;
230
231    fn from_str(s: &str) -> Result<Self, Self::Err> {
232        let input = multiplier::<T>(parse(s)?);
233
234        match input.0 {
235            true => Ok(ByteUnit::IEC(input.1)),
236            false => Ok(ByteUnit::SI(input.1)),
237        }
238    }
239}
240
241impl<T> PartialOrd for ByteUnit<T>
242where
243    i64: From<T>,
244    T: Copy + PartialEq,
245{
246    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
247        let value = i64::from(self.val());
248        let other = i64::from(other.val());
249
250        value.partial_cmp(&other)
251    }
252}
253
254impl<T> PartialEq for ByteUnit<T>
255where
256    i64: From<T>,
257    T: Copy + PartialEq,
258{
259    fn eq(&self, other: &Self) -> bool {
260        other.val().eq(&self.val())
261    }
262}
263
264impl<T> Add for ByteUnit<T>
265where
266    i64: From<T>,
267    T: Copy + Add<Output = T>,
268{
269    type Output = Self;
270
271    fn add(self, input: Self) -> Self::Output {
272        match self {
273            Self::IEC(value) => Self::IEC(value + input.val()),
274            Self::SI(value) => Self::SI(value + input.val()),
275        }
276    }
277}
278
279impl<T> Sub for ByteUnit<T>
280where
281    i64: From<T>,
282    T: Copy + Sub<Output = T>,
283{
284    type Output = Self;
285
286    fn sub(self, input: Self) -> Self::Output {
287        match self {
288            Self::IEC(value) => Self::IEC(value - input.val()),
289            Self::SI(value) => Self::SI(value - input.val()),
290        }
291    }
292}
293
294impl<T> Mul for ByteUnit<T>
295where
296    i64: From<T>,
297    T: Copy + Mul<Output = T>,
298{
299    type Output = Self;
300
301    fn mul(self, input: Self) -> Self::Output {
302        match self {
303            Self::IEC(value) => Self::IEC(value * input.val()),
304            Self::SI(value) => Self::SI(value * input.val()),
305        }
306    }
307}
308
309impl<T> Div for ByteUnit<T>
310where
311    i64: From<T>,
312    T: Copy + Div<Output = T>,
313{
314    type Output = Self;
315
316    fn div(self, input: Self) -> Self::Output {
317        match self {
318            Self::IEC(value) => Self::IEC(value / input.val()),
319            Self::SI(value) => Self::SI(value / input.val()),
320        }
321    }
322}