byte_unit/byte/decimal.rs
1use rust_decimal::prelude::*;
2
3use super::Byte;
4use crate::{common::is_zero_remainder_decimal, Unit};
5
6const DECIMAL_EIGHT: Decimal = Decimal::from_parts(8, 0, 0, false, 0);
7
8/// Associated functions for building `Byte` instances using `Decimal`.
9impl Byte {
10 /// Create a new `Byte` instance from a size in bytes.
11 ///
12 /// # Examples
13 ///
14 /// ```
15 /// use byte_unit::Byte;
16 /// use rust_decimal::Decimal;
17 ///
18 /// let byte = Byte::from_decimal(Decimal::from(15000000u64)).unwrap(); // 15 MB
19 /// ```
20 ///
21 /// # Points to Note
22 ///
23 /// * If the input **size** is too large (the maximum is **10<sup>27</sup> - 1** if the `u128` feature is enabled, or **2<sup>64</sup> - 1** otherwise) or not greater than or equal to **0**, this function will return `None`.
24 /// * The fractional part will be rounded up.
25 #[inline]
26 pub fn from_decimal(size: Decimal) -> Option<Self> {
27 if size >= Decimal::ZERO {
28 #[cfg(feature = "u128")]
29 {
30 let size = size.ceil();
31
32 match size.to_u128() {
33 Some(n) => Self::from_u128(n),
34 None => None,
35 }
36 }
37
38 #[cfg(not(feature = "u128"))]
39 {
40 let size = size.ceil();
41
42 size.to_u64().map(Self::from_u64)
43 }
44 } else {
45 None
46 }
47 }
48}
49
50/// Associated functions for building `Byte` instances using `Decimal` (with `Unit`).
51impl Byte {
52 /// Create a new `Byte` instance from a size of bytes with a unit.
53 ///
54 /// # Examples
55 ///
56 /// ```
57 /// use byte_unit::{Byte, Unit};
58 /// use rust_decimal::Decimal;
59 ///
60 /// let byte = Byte::from_decimal_with_unit(Decimal::from(15u64), Unit::MB).unwrap(); // 15 MB
61 /// ```
62 ///
63 /// # Points to Note
64 ///
65 /// * If the calculated byte is too large or not greater than or equal to **0**, this function will return `None`.
66 /// * The calculated byte will be rounded up.
67 #[inline]
68 pub fn from_decimal_with_unit(size: Decimal, unit: Unit) -> Option<Self> {
69 let v = {
70 match unit {
71 Unit::Bit => (size / DECIMAL_EIGHT).ceil(),
72 Unit::B => size,
73 _ => match size.checked_mul(Decimal::from(unit.as_bytes_u128())) {
74 Some(v) => v,
75 None => return None,
76 },
77 }
78 };
79
80 Self::from_decimal(v)
81 }
82}
83
84/// Methods for finding an unit using `Decimal`.
85impl Byte {
86 /// Find the appropriate unit and value that can be used to recover back to this `Byte` precisely.
87 ///
88 /// # Examples
89 ///
90 /// ```
91 /// use byte_unit::{Byte, Unit};
92 ///
93 /// let byte = Byte::from_u64(3670016);
94 ///
95 /// assert_eq!(
96 /// (3.5f64.try_into().unwrap(), Unit::MiB),
97 /// byte.get_recoverable_unit(false, 3)
98 /// );
99 /// ```
100 ///
101 /// ```
102 /// use byte_unit::{Byte, Unit};
103 ///
104 /// let byte = Byte::from_u64(437500);
105 ///
106 /// assert_eq!(
107 /// (3.5f64.try_into().unwrap(), Unit::Mbit),
108 /// byte.get_recoverable_unit(true, 3)
109 /// );
110 /// ```
111 ///
112 /// ```
113 /// use byte_unit::{Byte, Unit};
114 ///
115 /// let byte = Byte::from_u64(437500);
116 ///
117 /// assert_eq!(
118 /// (437.5f64.try_into().unwrap(), Unit::KB),
119 /// byte.get_recoverable_unit(false, 3)
120 /// );
121 /// ```
122 ///
123 /// # Points to Note
124 ///
125 /// * `precision` should be smaller or equal to `26` if the `u128` feature is enabled, otherwise `19`. The typical `precision` is `3`.
126 #[inline]
127 pub fn get_recoverable_unit(
128 self,
129 allow_in_bits: bool,
130 mut precision: usize,
131 ) -> (Decimal, Unit) {
132 let bytes_v = self.as_u128();
133 let bytes_vd = Decimal::from(bytes_v);
134
135 let a = if allow_in_bits { Unit::get_multiples() } else { Unit::get_multiples_bytes() };
136 let mut i = a.len() - 1;
137
138 if precision >= 28 {
139 precision = 28;
140 }
141
142 loop {
143 let unit = a[i];
144
145 let unit_v = unit.as_bytes_u128();
146
147 if bytes_v >= unit_v {
148 let unit_vd = Decimal::from(unit_v);
149
150 if let Some(quotient) = is_zero_remainder_decimal(bytes_vd, unit_vd, precision) {
151 return (quotient, unit);
152 }
153 }
154
155 if i == 0 {
156 break;
157 }
158
159 i -= 1;
160 }
161
162 (bytes_vd, Unit::B)
163 }
164}