byte_unit/bit/decimal.rs
1use rust_decimal::prelude::*;
2
3use super::Bit;
4use crate::{common::is_zero_remainder_decimal, Unit};
5
6/// Associated functions for building `Bit` instances using `Decimal`.
7impl Bit {
8 /// Create a new `Bit` instance from a size in bits.
9 ///
10 /// # Examples
11 ///
12 /// ```
13 /// use byte_unit::Bit;
14 /// use rust_decimal::Decimal;
15 ///
16 /// let bit = Bit::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 `Bit` instances using `Decimal` (with `Unit`).
49impl Bit {
50 /// Create a new `Bit` instance from a size of bits with a unit.
51 ///
52 /// # Examples
53 ///
54 /// ```
55 /// use byte_unit::{Bit, Unit};
56 /// use rust_decimal::Decimal;
57 ///
58 /// let bit = Bit::from_decimal_with_unit(Decimal::from(15u64), Unit::Mbit).unwrap(); // 15 Mb
59 /// ```
60 ///
61 /// # Points to Note
62 ///
63 /// * If the calculated bit is too large or not greater than or equal to **0**, this function will return `None`.
64 /// * The calculated bit 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 => size,
70 _ => size.checked_mul(Decimal::from(unit.as_bits_u128()))?,
71 }
72 };
73
74 Self::from_decimal(v)
75 }
76}
77
78/// Methods for finding an unit using `Decimal`.
79impl Bit {
80 /// Find the appropriate unit and value that can be used to recover back to this `Bit` precisely.
81 ///
82 /// # Examples
83 ///
84 /// ```
85 /// use byte_unit::{Bit, Unit};
86 ///
87 /// let bit = Bit::from_u64(3670016);
88 ///
89 /// assert_eq!(
90 /// (3.5f64.try_into().unwrap(), Unit::Mibit),
91 /// bit.get_recoverable_unit(false, 3)
92 /// );
93 /// ```
94 ///
95 /// ```
96 /// use byte_unit::{Bit, Unit};
97 ///
98 /// let bit = Bit::from_u64(28000000);
99 ///
100 /// assert_eq!(
101 /// (3.5f64.try_into().unwrap(), Unit::MB),
102 /// bit.get_recoverable_unit(true, 3)
103 /// );
104 /// ```
105 ///
106 /// ```
107 /// use byte_unit::{Bit, Unit};
108 ///
109 /// let bit = Bit::from_u64(437500);
110 ///
111 /// assert_eq!(
112 /// (437.5f64.try_into().unwrap(), Unit::Kbit),
113 /// bit.get_recoverable_unit(false, 3)
114 /// );
115 /// ```
116 ///
117 /// # Points to Note
118 ///
119 /// * `precision` should be smaller or equal to `26` if the `u128` feature is enabled, otherwise `19`. The typical `precision` is `3`.
120 #[inline]
121 pub fn get_recoverable_unit(
122 self,
123 allow_in_bytes: bool,
124 mut precision: usize,
125 ) -> (Decimal, Unit) {
126 let bits_v = self.as_u128();
127 let bits_vd = Decimal::from(bits_v);
128
129 let a = if allow_in_bytes { Unit::get_multiples() } else { Unit::get_multiples_bits() };
130 let mut i = a.len() - 1;
131
132 if precision >= 28 {
133 precision = 28;
134 }
135
136 loop {
137 let unit = a[i];
138
139 let unit_v = unit.as_bits_u128();
140
141 if bits_v >= unit_v {
142 let unit_vd = Decimal::from(unit_v);
143
144 if let Some(quotient) = is_zero_remainder_decimal(bits_vd, unit_vd, precision) {
145 return (quotient, unit);
146 }
147 }
148
149 if i == 0 {
150 break;
151 }
152
153 i -= 1;
154 }
155
156 (bits_vd, Unit::Bit)
157 }
158}