Skip to main content

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}