byte_unit/byte/adjusted/mod.rs
1mod built_in_traits;
2#[cfg(feature = "rocket")]
3mod rocket_traits;
4#[cfg(feature = "schemars")]
5mod schemars_traits;
6#[cfg(feature = "serde")]
7mod serde_traits;
8
9use core::{
10 cmp::Ordering,
11 fmt::{self, Alignment, Display, Formatter, Write},
12};
13
14use super::{Byte, Unit};
15use crate::{UnitType, common::round_fractional_part_f64};
16
17/// Generated from the [`Byte::get_adjusted_unit`](./struct.Byte.html#method.get_adjusted_unit) method or the the [`Byte::get_appropriate_unit`](./struct.Byte.html#method.get_appropriate_unit) method.
18///
19/// For accuracy representation, utilize the `Byte` struct.
20#[derive(Debug, Clone, Copy)]
21pub struct AdjustedByte {
22 pub(crate) value: f64,
23 pub(crate) unit: Unit,
24}
25
26impl PartialEq for AdjustedByte {
27 #[inline]
28 fn eq(&self, other: &AdjustedByte) -> bool {
29 let s = self.get_byte();
30 let o = other.get_byte();
31
32 s.eq(&o)
33 }
34}
35
36impl Eq for AdjustedByte {}
37
38impl PartialOrd for AdjustedByte {
39 #[inline]
40 fn partial_cmp(&self, other: &AdjustedByte) -> Option<Ordering> {
41 Some(self.cmp(other))
42 }
43}
44
45impl Ord for AdjustedByte {
46 #[inline]
47 fn cmp(&self, other: &AdjustedByte) -> Ordering {
48 let s = self.get_byte();
49 let o = other.get_byte();
50
51 s.cmp(&o)
52 }
53}
54
55impl Display for AdjustedByte {
56 /// Formats the value using the given formatter.
57 ///
58 /// # Examples
59 ///
60 /// ```
61 /// use byte_unit::{Byte, Unit};
62 ///
63 /// let byte = Byte::from_u64_with_unit(1555, Unit::KB).unwrap();
64 ///
65 /// let adjusted_byte = byte.get_adjusted_unit(Unit::MB);
66 ///
67 /// assert_eq!("1.555 MB", adjusted_byte.to_string());
68 /// ```
69 ///
70 /// ```
71 /// use byte_unit::{Byte, UnitType};
72 ///
73 /// let byte = Byte::from_u64(10000);
74 ///
75 /// let adjusted_byte_based_2 = byte.get_appropriate_unit(UnitType::Binary);
76 /// let adjusted_byte_based_10 = byte.get_appropriate_unit(UnitType::Decimal);
77 ///
78 /// assert_eq!("9.765625 KiB", format!("{adjusted_byte_based_2}"));
79 /// assert_eq!("10 KB", format!("{adjusted_byte_based_10}"));
80 ///
81 /// // with precision
82 /// assert_eq!("9.77 KiB", format!("{adjusted_byte_based_2:.2}"));
83 /// assert_eq!("10.00 KB", format!("{adjusted_byte_based_10:.2}"));
84 ///
85 /// // without any unnecessary fractional part
86 /// assert_eq!("9.77 KiB", format!("{adjusted_byte_based_2:#.2}"));
87 /// assert_eq!("10 KB", format!("{adjusted_byte_based_10:#.2}"));
88 ///
89 /// // with a width, left alignment
90 /// assert_eq!("9.77 KiB", format!("{adjusted_byte_based_2:10.2}"));
91 /// assert_eq!("10.00 KB", format!("{adjusted_byte_based_10:10.2}"));
92 ///
93 /// // with a width, right alignment
94 /// assert_eq!(" 9.77 KiB", format!("{adjusted_byte_based_2:>10.2}"));
95 /// assert_eq!(" 10.00 KB", format!("{adjusted_byte_based_10:>10.2}"));
96 ///
97 /// // with a width, right alignment, more spaces between the value and the unit
98 /// assert_eq!(" 9.77 KiB", format!("{adjusted_byte_based_2:>+10.2}"));
99 /// assert_eq!(" 10.00 KB", format!("{adjusted_byte_based_10:>+10.2}"));
100 ///
101 /// // no spaces between the value and the unit
102 /// assert_eq!("9.765625KiB", format!("{adjusted_byte_based_2:-}"));
103 /// assert_eq!("10KB", format!("{adjusted_byte_based_10:-}"));
104 /// ```
105 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
106 let Self {
107 value,
108 unit,
109 } = self;
110 let handle_basic_precision = |precision: usize, f: &mut Formatter<'_>| -> fmt::Result {
111 if f.alternate() {
112 let value = round_fractional_part_f64(*value, precision);
113
114 f.write_fmt(format_args!("{value}"))
115 } else if matches!(unit, Unit::Bit | Unit::B) {
116 f.write_fmt(format_args!("{value}"))
117 } else {
118 f.write_fmt(format_args!("{value:.precision$}"))
119 }
120 };
121
122 let space_length = if f.sign_plus() {
123 4 - unit.as_str().len()
124 } else if f.sign_minus() {
125 0
126 } else {
127 1
128 };
129
130 if let Some(mut width) = f.width() {
131 let l = unit.as_str().len() + space_length;
132
133 if let Some(precision) = f.precision() {
134 if width > l + 1 {
135 width -= l;
136
137 let alignment = f.align().unwrap_or(Alignment::Left);
138
139 if f.alternate() {
140 let value = round_fractional_part_f64(*value, precision);
141
142 match alignment {
143 Alignment::Left | Alignment::Center => {
144 f.write_fmt(format_args!("{value:<width$}"))?
145 },
146 Alignment::Right => f.write_fmt(format_args!("{value:>width$}"))?,
147 }
148 } else if matches!(unit, Unit::Bit | Unit::B) {
149 match alignment {
150 Alignment::Left | Alignment::Center => {
151 f.write_fmt(format_args!("{value:<width$}"))?
152 },
153 Alignment::Right => f.write_fmt(format_args!("{value:>width$}"))?,
154 }
155 } else {
156 match alignment {
157 Alignment::Left | Alignment::Center => {
158 f.write_fmt(format_args!("{value:<width$.precision$}"))?
159 },
160 Alignment::Right => {
161 f.write_fmt(format_args!("{value:>width$.precision$}"))?
162 },
163 }
164 }
165 } else {
166 handle_basic_precision(precision, f)?;
167 }
168 } else if width > l + 1 {
169 width -= l;
170
171 let alignment = f.align().unwrap_or(Alignment::Left);
172
173 match alignment {
174 Alignment::Left | Alignment::Center => {
175 f.write_fmt(format_args!("{value:<width$}"))?
176 },
177 Alignment::Right => f.write_fmt(format_args!("{value:>width$}"))?,
178 }
179 } else {
180 f.write_fmt(format_args!("{value}"))?;
181 }
182 } else if let Some(precision) = f.precision() {
183 handle_basic_precision(precision, f)?;
184 } else {
185 f.write_fmt(format_args!("{value}"))?;
186 }
187
188 for _ in 0..space_length {
189 f.write_char(' ')?;
190 }
191
192 f.write_fmt(format_args!("{unit}"))
193 }
194}
195
196/// Methods for getting values.
197impl AdjustedByte {
198 /// Get the value.
199 #[inline]
200 pub const fn get_value(&self) -> f64 {
201 self.value
202 }
203
204 /// Get the unit.
205 #[inline]
206 pub const fn get_unit(&self) -> Unit {
207 self.unit
208 }
209
210 /// Create a new `Byte` instance from this `AdjustedByte` instance.
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// use byte_unit::{Byte, Unit};
216 ///
217 /// let byte = Byte::from_u64_with_unit(1555, Unit::KB).unwrap();
218 ///
219 /// let adjusted_byte = byte.get_adjusted_unit(Unit::MB);
220 ///
221 /// let byte_back = adjusted_byte.get_byte();
222 ///
223 /// assert_eq!(byte, byte_back);
224 /// ```
225 ///
226 /// # Points to Note
227 ///
228 /// * Values rounded above the supported range return the maximum value.
229 /// * The result may not be logically equal to the original `Byte` instance due to the accuracy of floating-point numbers.
230 #[inline]
231 pub fn get_byte(&self) -> Byte {
232 Byte::from_f64_with_unit(self.value, self.unit).unwrap_or(Byte::MAX)
233 }
234}
235
236/// Associated functions for generating `AdjustedByte`.
237impl Byte {
238 /// Adjust the unit and value for this `Byte` instance.
239 ///
240 /// # Examples
241 ///
242 /// ```
243 /// use byte_unit::{AdjustedByte, Byte, Unit};
244 ///
245 /// let byte = Byte::parse_str("123KiB", true).unwrap();
246 ///
247 /// let adjusted_byte = byte.get_adjusted_unit(Unit::KB);
248 ///
249 /// assert_eq!("125.952 KB", adjusted_byte.to_string());
250 /// ```
251 ///
252 /// ```
253 /// use byte_unit::{AdjustedByte, Byte, Unit};
254 ///
255 /// let byte = Byte::parse_str("50.84 MB", true).unwrap();
256 ///
257 /// let adjusted_byte = byte.get_adjusted_unit(Unit::MiB);
258 ///
259 /// assert_eq!("48.48480224609375 MiB", adjusted_byte.to_string());
260 /// ```
261 #[inline]
262 pub fn get_adjusted_unit(self, unit: Unit) -> AdjustedByte {
263 let byte_v = self.as_u128();
264
265 let value = match unit {
266 Unit::Bit => (byte_v << 3) as f64,
267 Unit::B => byte_v as f64,
268 _ => byte_v as f64 / unit.as_bytes_u128() as f64,
269 };
270
271 AdjustedByte {
272 value,
273 unit,
274 }
275 }
276
277 /// Find the appropriate unit and value for this `Byte` instance.
278 ///
279 /// # Examples
280 ///
281 /// ```
282 /// use byte_unit::{Byte, UnitType};
283 ///
284 /// let byte = Byte::parse_str("123KiB", true).unwrap();
285 ///
286 /// let adjusted_byte = byte.get_appropriate_unit(UnitType::Decimal);
287 ///
288 /// assert_eq!("125.952 KB", adjusted_byte.to_string());
289 /// ```
290 ///
291 /// ```
292 /// use byte_unit::{Byte, UnitType};
293 ///
294 /// let byte = Byte::parse_str("50.84 MB", true).unwrap();
295 ///
296 /// let adjusted_byte = byte.get_appropriate_unit(UnitType::Binary);
297 ///
298 /// assert_eq!("48.48480224609375 MiB", adjusted_byte.to_string());
299 /// ```
300 pub fn get_appropriate_unit(&self, unit_type: UnitType) -> AdjustedByte {
301 let a = Unit::get_multiples_bytes();
302
303 let (skip, step) = match unit_type {
304 UnitType::Binary => (0, 2),
305 UnitType::Decimal => (1, 2),
306 UnitType::Both => (0, 1),
307 };
308
309 let bytes_v = self.as_u128();
310
311 for unit in a.iter().rev().skip(skip).step_by(step) {
312 if bytes_v >= unit.as_bytes_u128() {
313 return self.get_adjusted_unit(*unit);
314 }
315 }
316
317 self.get_adjusted_unit(Unit::B)
318 }
319}