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