human_bandwidth/
lib.rs

1//! Human-friendly bandwidth parser and formatter
2//!
3//! ## Facilities:
4//!
5//! * Parses bandwidth in free form like `2Gbps 340Mbps` or `2.34Gbps`
6//! * Formats bandwidth in similar form `150.024kbps` (default) or `150kbps 24bps` (with feature `display-integer` enabled)
7//!
8//! ## Features
9//!
10//! * Enable `serde` feature for serde integration.
11//! * Enable `display-integer` feature to display integer values only.
12
13use std::error::Error as StdError;
14use std::fmt;
15use std::str::Chars;
16
17#[cfg(feature = "serde")]
18pub mod option;
19#[cfg(feature = "serde")]
20pub mod serde;
21
22/// Reexport module
23pub mod re {
24    pub use bandwidth;
25}
26
27use bandwidth::Bandwidth;
28
29const FRACTION_PART_LIMIT: u32 = 12;
30
31/// Error parsing human-friendly bandwidth
32#[derive(Debug, PartialEq, Clone)]
33pub enum Error {
34    /// Invalid character during parsing
35    ///
36    /// More specifically anything that is not alphanumeric is prohibited
37    ///
38    /// The field is an byte offset of the character in the string.
39    InvalidCharacter(usize),
40    /// Non-numeric value where number is expected
41    ///
42    /// This usually means that either bandwidth unit is broken into words,
43    /// e.g. `M bps` instead of `Mbps`, or just number is omitted,
44    /// for example `2 Mbps kbps` instead of `2 Mbps 1 kbps`
45    ///
46    /// The field is an byte offset of the erroneous character
47    /// in the string.
48    NumberExpected(usize),
49    /// Unit in the number is not one of allowed units
50    ///
51    /// See documentation of `parse_bandwidth` for the list of supported
52    /// bandwidth units.
53    ///
54    /// The two fields are start and end (exclusive) of the slice from
55    /// the original string, containing erroneous value
56    UnknownUnit {
57        /// Start of the invalid unit inside the original string
58        start: usize,
59        /// End of the invalid unit inside the original string
60        end: usize,
61        /// The unit verbatim
62        unit: String,
63        /// A number associated with the unit
64        value: u64,
65    },
66    /// The numeric value is too large
67    ///
68    /// Usually this means value is too large to be useful.
69    NumberOverflow,
70    /// The value was an empty string (or consists only whitespace)
71    Empty,
72}
73
74impl StdError for Error {}
75
76impl fmt::Display for Error {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        match self {
79            Error::InvalidCharacter(offset) => write!(f, "invalid character at {}", offset),
80            Error::NumberExpected(offset) => write!(f, "expected number at {}", offset),
81            Error::UnknownUnit { unit, value, .. } if unit.is_empty() => {
82                write!(
83                    f,
84                    "bandwidth unit needed, for example {0}Mbps or {0}bps",
85                    value,
86                )
87            }
88            Error::UnknownUnit { unit, .. } => {
89                write!(
90                    f,
91                    "unknown bandwidth unit {:?}, \
92                    supported units: bps, kbps, Mbps, Gbps, Tbps",
93                    unit
94                )
95            }
96            Error::NumberOverflow => write!(f, "number is too large"),
97            Error::Empty => write!(f, "value was empty"),
98        }
99    }
100}
101
102/// A wrapper type that allows you to Display a Bandwidth
103#[derive(Debug, Clone)]
104pub struct FormattedBandwidth(Bandwidth);
105
106trait OverflowOp: Sized {
107    fn mul(self, other: Self) -> Result<Self, Error>;
108    fn add(self, other: Self) -> Result<Self, Error>;
109}
110
111impl OverflowOp for u64 {
112    fn mul(self, other: Self) -> Result<Self, Error> {
113        self.checked_mul(other).ok_or(Error::NumberOverflow)
114    }
115    fn add(self, other: Self) -> Result<Self, Error> {
116        self.checked_add(other).ok_or(Error::NumberOverflow)
117    }
118}
119
120fn parse_fraction(fraction: u64, fraction_cnt: u32, need_digit: u32) -> u64 {
121    if need_digit >= fraction_cnt {
122        fraction * 10u64.pow(need_digit - fraction_cnt)
123    } else {
124        fraction / 10u64.pow(fraction_cnt - need_digit)
125    }
126}
127
128struct Parser<'a> {
129    iter: Chars<'a>,
130    src: &'a str,
131    current: (u64, u64),
132}
133
134impl Parser<'_> {
135    fn off(&self) -> usize {
136        self.src.len() - self.iter.as_str().len()
137    }
138
139    fn parse_first_char(&mut self) -> Result<Option<u64>, Error> {
140        let off = self.off();
141        for c in self.iter.by_ref() {
142            match c {
143                '0'..='9' => {
144                    return Ok(Some(c as u64 - '0' as u64));
145                }
146                c if c.is_whitespace() => continue,
147                _ => {
148                    return Err(Error::NumberExpected(off));
149                }
150            }
151        }
152        Ok(None)
153    }
154
155    fn parse_unit(
156        &mut self,
157        n: u64,
158        fraction: u64,
159        fraction_cnt: u32,
160        start: usize,
161        end: usize,
162    ) -> Result<(), Error> {
163        let (mut gbps, bps) = match &self.src[start..end] {
164            "bps" | "bit/s" | "b/s" => (0u64, n),
165            "kbps" | "Kbps" | "kbit/s" | "Kbit/s" | "kb/s" | "Kb/s" => (
166                0u64,
167                n.mul(1000)?
168                    .add(parse_fraction(fraction, fraction_cnt, 3))?,
169            ),
170            "Mbps" | "mbps" | "Mbit/s" | "mbit/s" | "Mb/s" | "mb/s" => (
171                0u64,
172                n.mul(1_000_000)?
173                    .add(parse_fraction(fraction, fraction_cnt, 6))?,
174            ),
175            "Gbps" | "gbps" | "Gbit/s" | "gbit/s" | "Gb/s" | "gb/s" => {
176                (n, parse_fraction(fraction, fraction_cnt, 9))
177            }
178            "Tbps" | "tbps" | "Tbit/s" | "tbit/s" | "Tb/s" | "tb/s" => {
179                let bps = parse_fraction(fraction, fraction_cnt, 12);
180                (n.mul(1000)?.add(bps / 1_000_000_000)?, bps % 1_000_000_000)
181            }
182            _ => {
183                return Err(Error::UnknownUnit {
184                    start,
185                    end,
186                    unit: self.src[start..end].to_string(),
187                    value: n,
188                });
189            }
190        };
191        let mut bps = self.current.1.add(bps)?;
192        if bps > 1_000_000_000 {
193            gbps = gbps.add(bps / 1_000_000_000)?;
194            bps %= 1_000_000_000;
195        }
196        gbps = self.current.0.add(gbps)?;
197        self.current = (gbps, bps);
198        Ok(())
199    }
200
201    fn parse(mut self) -> Result<Bandwidth, Error> {
202        let mut n = self.parse_first_char()?.ok_or(Error::Empty)?;
203        let mut decimal = false;
204        let mut fraction: u64 = 0;
205        let mut fraction_cnt: u32 = 0;
206        'outer: loop {
207            let mut off = self.off();
208            while let Some(c) = self.iter.next() {
209                match c {
210                    '0'..='9' => {
211                        if decimal {
212                            if fraction_cnt >= FRACTION_PART_LIMIT {
213                                continue;
214                            }
215                            fraction = fraction
216                                .checked_mul(10)
217                                .and_then(|x| x.checked_add(c as u64 - '0' as u64))
218                                .ok_or(Error::NumberOverflow)?;
219                            fraction_cnt += 1;
220                        } else {
221                            n = n
222                                .checked_mul(10)
223                                .and_then(|x| x.checked_add(c as u64 - '0' as u64))
224                                .ok_or(Error::NumberOverflow)?;
225                        }
226                    }
227                    c if c.is_whitespace() => {}
228                    '.' => {
229                        if decimal {
230                            return Err(Error::InvalidCharacter(off));
231                        }
232                        decimal = true;
233                    }
234                    'a'..='z' | 'A'..='Z' | '/' => {
235                        break;
236                    }
237                    _ => {
238                        return Err(Error::InvalidCharacter(off));
239                    }
240                }
241                off = self.off();
242            }
243            let start = off;
244            let mut off = self.off();
245            while let Some(c) = self.iter.next() {
246                match c {
247                    '0'..='9' => {
248                        self.parse_unit(n, fraction, fraction_cnt, start, off)?;
249                        n = c as u64 - '0' as u64;
250                        fraction = 0;
251                        decimal = false;
252                        fraction_cnt = 0;
253                        continue 'outer;
254                    }
255                    c if c.is_whitespace() => break,
256                    'a'..='z' | 'A'..='Z' | '/' => {}
257                    _ => {
258                        return Err(Error::InvalidCharacter(off));
259                    }
260                }
261                off = self.off();
262            }
263            self.parse_unit(n, fraction, fraction_cnt, start, off)?;
264            n = match self.parse_first_char()? {
265                Some(n) => n,
266                None => return Ok(Bandwidth::new(self.current.0, self.current.1 as u32)),
267            };
268            fraction = 0;
269            decimal = false;
270            fraction_cnt = 0;
271        }
272    }
273}
274
275/// Parse bandwidth object `1Gbps 12Mbps 5bps` or `1.012000005Gbps`
276///
277/// The bandwidth object is a concatenation of rate spans. Where each rate
278/// span is an number and a suffix. Supported suffixes:
279///
280/// * `bps`, `bit/s`, `b/s` -- bit per second
281/// * `kbps`, `kbit/s`, `kb/s` -- kilobit per second
282/// * `Mbps`, `Mbit/s`, `Mb/s` -- megabit per second
283/// * `Gbps`, `Gbit/s`, `Gb/s` -- gigabit per second
284/// * `Tbps`, `Tbit/s`, `Tb/s` -- terabit per second
285///
286/// While the number can be integer or decimal, the fractional part less than 1bps will always be
287/// ignored.
288///
289/// # Examples
290///
291/// ```
292/// use bandwidth::Bandwidth;
293/// use human_bandwidth::parse_bandwidth;
294///
295/// assert_eq!(parse_bandwidth("9Tbps 420Gbps"), Ok(Bandwidth::new(9420, 0)));
296/// assert_eq!(parse_bandwidth("32Mbps"), Ok(Bandwidth::new(0, 32_000_000)));
297/// assert_eq!(parse_bandwidth("150.024kbps"), Ok(Bandwidth::new(0, 150_024)));
298/// // The fractional part less than 1bps will always be ignored
299/// assert_eq!(parse_bandwidth("150.02456kbps"), Ok(Bandwidth::new(0, 150_024)));
300/// ```
301pub fn parse_bandwidth(s: &str) -> Result<Bandwidth, Error> {
302    Parser {
303        iter: s.chars(),
304        src: s,
305        current: (0, 0),
306    }
307    .parse()
308}
309
310/// Formats bandwidth into a human-readable string
311///
312/// Note: this format is guaranteed to have same value when using
313/// parse_bandwidth, but we can change some details of the exact composition
314/// of the value.
315///
316/// By default it will format the value with the largest possible unit in decimal form.
317/// If you want to display integer values only, enable the `display-integer` feature.
318///
319/// # Examples
320///
321/// ```
322/// use bandwidth::Bandwidth;
323/// use human_bandwidth::format_bandwidth;
324///
325/// // Enabling the `display-integer` feature will display integer values only
326/// # #[cfg(feature = "display-integer")]
327/// # {
328/// let val1 = Bandwidth::new(9420, 0);
329/// assert_eq!(format_bandwidth(val1).to_string(), "9Tbps 420Gbps");
330/// let val2 = Bandwidth::new(0, 32_000_000);
331/// assert_eq!(format_bandwidth(val2).to_string(), "32Mbps");
332/// # }
333///
334/// // Disabling the `display-integer` feature will display decimal values
335/// # #[cfg(not(feature = "display-integer"))]
336/// # {
337/// let val1 = Bandwidth::new(9420, 0);
338/// assert_eq!(format_bandwidth(val1).to_string(), "9.42Tbps");
339/// let val2 = Bandwidth::new(0, 32_000_000);
340/// assert_eq!(format_bandwidth(val2).to_string(), "32Mbps");
341/// # }
342/// ```
343pub fn format_bandwidth(val: Bandwidth) -> FormattedBandwidth {
344    FormattedBandwidth(val)
345}
346
347fn item(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u32) -> fmt::Result {
348    if value > 0 {
349        if *started {
350            f.write_str(" ")?;
351        }
352        write!(f, "{}{}", value, name)?;
353        *started = true;
354    }
355    Ok(())
356}
357
358#[derive(Copy, Clone)]
359#[repr(usize)]
360enum LargestUnit {
361    Bps = 0,
362    Kbps = 1,
363    Mbps = 2,
364    Gbps = 3,
365    Tbps = 4,
366}
367
368impl fmt::Display for LargestUnit {
369    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
370        match self {
371            LargestUnit::Bps => f.write_str("bps"),
372            LargestUnit::Kbps => f.write_str("kbps"),
373            LargestUnit::Mbps => f.write_str("Mbps"),
374            LargestUnit::Gbps => f.write_str("Gbps"),
375            LargestUnit::Tbps => f.write_str("Tbps"),
376        }
377    }
378}
379
380impl FormattedBandwidth {
381    /// Returns a reference to the [`Bandwidth`][] that is being formatted.
382    pub fn get_ref(&self) -> &Bandwidth {
383        &self.0
384    }
385
386    /// Enabling the `display-integer` feature will display integer values only
387    ///
388    /// This method is preserved for backward compatibility and custom formatting.
389    pub fn fmt_integer(&self, f: &mut fmt::Formatter) -> fmt::Result {
390        let gbps = self.0.as_gbps();
391        let bps = self.0.subgbps_bps();
392
393        if gbps == 0 && bps == 0 {
394            f.write_str("0bps")?;
395            return Ok(());
396        }
397
398        let tbps = gbps / 1_000;
399        let gbps = gbps % 1_000;
400
401        let mbps = bps / 1_000_000;
402        let kbps = bps / 1_000 % 1_000;
403        let bps = bps % 1_000;
404
405        let started = &mut false;
406        item(f, started, "Tbps", tbps as u32)?;
407        item(f, started, "Gbps", gbps as u32)?;
408        item(f, started, "Mbps", mbps)?;
409        item(f, started, "kbps", kbps)?;
410        item(f, started, "bps", bps)?;
411        Ok(())
412    }
413
414    /// Disabling the `display-integer` feature will display decimal values
415    ///
416    /// This method is preserved for custom formatting.
417    pub fn fmt_decimal(&self, f: &mut fmt::Formatter) -> fmt::Result {
418        let gbps = self.0.as_gbps();
419        let bps = self.0.subgbps_bps();
420
421        if gbps == 0 && bps == 0 {
422            f.write_str("0bps")?;
423            return Ok(());
424        }
425
426        let tbps = gbps / 1_000;
427        let gbps = gbps % 1_000;
428
429        let mbps = (bps / 1_000_000) as u64;
430        let kbps = (bps / 1_000 % 1_000) as u64;
431        let bps = (bps % 1_000) as u64;
432
433        let largest_unit = if tbps > 0 {
434            LargestUnit::Tbps
435        } else if gbps > 0 {
436            LargestUnit::Gbps
437        } else if mbps > 0 {
438            LargestUnit::Mbps
439        } else if kbps > 0 {
440            LargestUnit::Kbps
441        } else {
442            LargestUnit::Bps
443        };
444
445        let values = [bps, kbps, mbps, gbps, tbps];
446        let mut index = largest_unit as usize;
447        let mut zeros = 0;
448        let mut dot = true;
449        write!(f, "{}", values[index])?;
450        loop {
451            if index == 0 {
452                write!(f, "{}", largest_unit)?;
453                break;
454            }
455            index -= 1;
456            let value = values[index];
457            if value == 0 {
458                zeros += 3;
459                continue;
460            } else {
461                if dot {
462                    f.write_str(".")?;
463                    dot = false;
464                }
465                if zeros > 0 {
466                    write!(f, "{:0width$}", 0, width = zeros)?;
467                    zeros = 0;
468                }
469                if value % 10 != 0 {
470                    write!(f, "{:03}", value)?;
471                } else if value % 100 != 0 {
472                    write!(f, "{:02}", value / 10)?;
473                    zeros += 1;
474                } else {
475                    write!(f, "{}", value / 100)?;
476                    zeros += 2;
477                }
478            }
479        }
480        Ok(())
481    }
482}
483
484impl fmt::Display for FormattedBandwidth {
485    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
486        #[cfg(not(feature = "display-integer"))]
487        self.fmt_decimal(f)?;
488        #[cfg(feature = "display-integer")]
489        self.fmt_integer(f)?;
490        Ok(())
491    }
492}
493
494#[cfg(test)]
495mod tests {
496    use super::*;
497    use bandwidth::Bandwidth;
498
499    #[test]
500    fn test_units() {
501        assert_eq!(parse_bandwidth("1bps"), Ok(Bandwidth::new(0, 1)));
502        assert_eq!(parse_bandwidth("2bit/s"), Ok(Bandwidth::new(0, 2)));
503        assert_eq!(parse_bandwidth("15b/s"), Ok(Bandwidth::new(0, 15)));
504        assert_eq!(parse_bandwidth("51kbps"), Ok(Bandwidth::new(0, 51_000)));
505        assert_eq!(parse_bandwidth("79Kbps"), Ok(Bandwidth::new(0, 79_000)));
506        assert_eq!(parse_bandwidth("81kbit/s"), Ok(Bandwidth::new(0, 81_000)));
507        assert_eq!(parse_bandwidth("100Kbit/s"), Ok(Bandwidth::new(0, 100_000)));
508        assert_eq!(parse_bandwidth("150kb/s"), Ok(Bandwidth::new(0, 150_000)));
509        assert_eq!(parse_bandwidth("410Kb/s"), Ok(Bandwidth::new(0, 410_000)));
510        assert_eq!(parse_bandwidth("12Mbps"), Ok(Bandwidth::new(0, 12_000_000)));
511        assert_eq!(parse_bandwidth("16mbps"), Ok(Bandwidth::new(0, 16_000_000)));
512        assert_eq!(
513            parse_bandwidth("24Mbit/s"),
514            Ok(Bandwidth::new(0, 24_000_000))
515        );
516        assert_eq!(
517            parse_bandwidth("36mbit/s"),
518            Ok(Bandwidth::new(0, 36_000_000))
519        );
520        assert_eq!(parse_bandwidth("48Mb/s"), Ok(Bandwidth::new(0, 48_000_000)));
521        assert_eq!(parse_bandwidth("96mb/s"), Ok(Bandwidth::new(0, 96_000_000)));
522        assert_eq!(parse_bandwidth("2Gbps"), Ok(Bandwidth::new(2, 0)));
523        assert_eq!(parse_bandwidth("4gbps"), Ok(Bandwidth::new(4, 0)));
524        assert_eq!(parse_bandwidth("6Gbit/s"), Ok(Bandwidth::new(6, 0)));
525        assert_eq!(parse_bandwidth("8gbit/s"), Ok(Bandwidth::new(8, 0)));
526        assert_eq!(parse_bandwidth("16Gb/s"), Ok(Bandwidth::new(16, 0)));
527        assert_eq!(parse_bandwidth("40gb/s"), Ok(Bandwidth::new(40, 0)));
528        assert_eq!(parse_bandwidth("1Tbps"), Ok(Bandwidth::new(1_000, 0)));
529        assert_eq!(parse_bandwidth("2tbps"), Ok(Bandwidth::new(2_000, 0)));
530        assert_eq!(parse_bandwidth("4Tbit/s"), Ok(Bandwidth::new(4_000, 0)));
531        assert_eq!(parse_bandwidth("8tbit/s"), Ok(Bandwidth::new(8_000, 0)));
532        assert_eq!(parse_bandwidth("16Tb/s"), Ok(Bandwidth::new(16_000, 0)));
533        assert_eq!(parse_bandwidth("32tb/s"), Ok(Bandwidth::new(32_000, 0)));
534    }
535
536    #[test]
537    fn test_decimal() {
538        assert_eq!(parse_bandwidth("1.5bps"), Ok(Bandwidth::new(0, 1)));
539        assert_eq!(parse_bandwidth("2.5bit/s"), Ok(Bandwidth::new(0, 2)));
540        assert_eq!(parse_bandwidth("15.5b/s"), Ok(Bandwidth::new(0, 15)));
541        assert_eq!(parse_bandwidth("51.6kbps"), Ok(Bandwidth::new(0, 51_600)));
542        assert_eq!(parse_bandwidth("79.78Kbps"), Ok(Bandwidth::new(0, 79_780)));
543        assert_eq!(
544            parse_bandwidth("81.923kbit/s"),
545            Ok(Bandwidth::new(0, 81_923))
546        );
547        assert_eq!(
548            parse_bandwidth("100.1234Kbit/s"),
549            Ok(Bandwidth::new(0, 100_123))
550        );
551        assert_eq!(
552            parse_bandwidth("150.12345kb/s"),
553            Ok(Bandwidth::new(0, 150_123))
554        );
555        assert_eq!(
556            parse_bandwidth("410.123456Kb/s"),
557            Ok(Bandwidth::new(0, 410_123))
558        );
559        assert_eq!(
560            parse_bandwidth("12.123Mbps"),
561            Ok(Bandwidth::new(0, 12_123_000))
562        );
563        assert_eq!(
564            parse_bandwidth("16.1234mbps"),
565            Ok(Bandwidth::new(0, 16_123_400))
566        );
567        assert_eq!(
568            parse_bandwidth("24.12345Mbit/s"),
569            Ok(Bandwidth::new(0, 24_123_450))
570        );
571        assert_eq!(
572            parse_bandwidth("36.123456mbit/s"),
573            Ok(Bandwidth::new(0, 36_123_456))
574        );
575        assert_eq!(
576            parse_bandwidth("48.123Mb/s"),
577            Ok(Bandwidth::new(0, 48_123_000))
578        );
579        assert_eq!(
580            parse_bandwidth("96.1234mb/s"),
581            Ok(Bandwidth::new(0, 96_123_400))
582        );
583        assert_eq!(
584            parse_bandwidth("2.123Gbps"),
585            Ok(Bandwidth::new(2, 123_000_000))
586        );
587        assert_eq!(
588            parse_bandwidth("4.1234gbps"),
589            Ok(Bandwidth::new(4, 123_400_000))
590        );
591        assert_eq!(
592            parse_bandwidth("6.12345Gbit/s"),
593            Ok(Bandwidth::new(6, 123_450_000))
594        );
595        assert_eq!(
596            parse_bandwidth("8.123456gbit/s"),
597            Ok(Bandwidth::new(8, 123_456_000))
598        );
599        assert_eq!(
600            parse_bandwidth("16.123456789Gb/s"),
601            Ok(Bandwidth::new(16, 123_456_789))
602        );
603        assert_eq!(
604            parse_bandwidth("40.12345678912gb/s"),
605            Ok(Bandwidth::new(40, 123_456_789))
606        );
607        assert_eq!(parse_bandwidth("1.123Tbps"), Ok(Bandwidth::new(1_123, 0)));
608        assert_eq!(
609            parse_bandwidth("2.1234tbps"),
610            Ok(Bandwidth::new(2_123, 400_000_000))
611        );
612        assert_eq!(
613            parse_bandwidth("4.12345Tbit/s"),
614            Ok(Bandwidth::new(4_123, 450_000_000))
615        );
616        assert_eq!(
617            parse_bandwidth("8.123456tbit/s"),
618            Ok(Bandwidth::new(8_123, 456_000_000))
619        );
620        assert_eq!(
621            parse_bandwidth("16.123456789Tb/s"),
622            Ok(Bandwidth::new(16_123, 456_789_000))
623        );
624        assert_eq!(
625            parse_bandwidth("32.12345678912tb/s"),
626            Ok(Bandwidth::new(32_123, 456_789_120))
627        );
628    }
629
630    #[test]
631    fn test_combo() {
632        assert_eq!(
633            parse_bandwidth("1bps 2bit/s 3b/s"),
634            Ok(Bandwidth::new(0, 6))
635        );
636        assert_eq!(
637            parse_bandwidth("4kbps 5Kbps 6kbit/s"),
638            Ok(Bandwidth::new(0, 15_000))
639        );
640        assert_eq!(
641            parse_bandwidth("7Mbps 8mbps 9Mbit/s"),
642            Ok(Bandwidth::new(0, 24_000_000))
643        );
644        assert_eq!(
645            parse_bandwidth("10Gbps 11gbps 12Gbit/s"),
646            Ok(Bandwidth::new(33, 0))
647        );
648        assert_eq!(
649            parse_bandwidth("13Tbps 14tbps 15Tbit/s"),
650            Ok(Bandwidth::new(42_000, 0))
651        );
652        assert_eq!(
653            parse_bandwidth("10Gbps 5Mbps 1b/s"),
654            Ok(Bandwidth::new(10, 5_000_001))
655        );
656        assert_eq!(
657            parse_bandwidth("36Mbps 12kbps 24bps"),
658            Ok(Bandwidth::new(0, 36_012_024))
659        );
660    }
661
662    #[test]
663    fn test_decimal_combo() {
664        assert_eq!(
665            parse_bandwidth("1.1bps 2.2bit/s 3.3b/s"),
666            Ok(Bandwidth::new(0, 6))
667        );
668        assert_eq!(
669            parse_bandwidth("4.4kbps 5.5Kbps 6.6kbit/s"),
670            Ok(Bandwidth::new(0, 16_500))
671        );
672        assert_eq!(
673            parse_bandwidth("7.7Mbps 8.8mbps 9.9Mbit/s"),
674            Ok(Bandwidth::new(0, 26_400_000))
675        );
676        assert_eq!(
677            parse_bandwidth("10.10Gbps 11.11gbps 12.12Gbit/s"),
678            Ok(Bandwidth::new(33, 330_000_000))
679        );
680        assert_eq!(
681            parse_bandwidth("13.13Tbps 14.14tbps 15.15Tbit/s"),
682            Ok(Bandwidth::new(42_420, 0))
683        );
684        assert_eq!(
685            parse_bandwidth("10.1Gbps 5.2Mbps 1.3b/s"),
686            Ok(Bandwidth::new(10, 105_200_001))
687        );
688        assert_eq!(
689            parse_bandwidth("36.1Mbps 12.2kbps 24.3bps"),
690            Ok(Bandwidth::new(0, 36_112_224))
691        );
692    }
693
694    #[test]
695    fn test_overflow() {
696        assert_eq!(
697            parse_bandwidth("100000000000000000000bps"),
698            Err(Error::NumberOverflow)
699        );
700        assert_eq!(
701            parse_bandwidth("100000000000000000kbps"),
702            Err(Error::NumberOverflow)
703        );
704        assert_eq!(
705            parse_bandwidth("100000000000000Mbps"),
706            Err(Error::NumberOverflow)
707        );
708        assert_eq!(
709            parse_bandwidth("100000000000000000000Gbps"),
710            Err(Error::NumberOverflow)
711        );
712        assert_eq!(
713            parse_bandwidth("10000000000000000000Tbps"),
714            Err(Error::NumberOverflow)
715        );
716    }
717
718    #[test]
719    fn test_nice_error_message() {
720        assert_eq!(
721            parse_bandwidth("123").unwrap_err().to_string(),
722            "bandwidth unit needed, for example 123Mbps or 123bps"
723        );
724        assert_eq!(
725            parse_bandwidth("10 Gbps 1").unwrap_err().to_string(),
726            "bandwidth unit needed, for example 1Mbps or 1bps"
727        );
728        assert_eq!(
729            parse_bandwidth("10 byte/s").unwrap_err().to_string(),
730            "unknown bandwidth unit \"byte/s\", \
731                    supported units: bps, kbps, Mbps, Gbps, Tbps"
732        );
733    }
734
735    #[test]
736    fn test_formatted_bandwidth_integer() {
737        struct TestInteger(FormattedBandwidth);
738        impl From<FormattedBandwidth> for TestInteger {
739            fn from(fb: FormattedBandwidth) -> Self {
740                TestInteger(fb)
741            }
742        }
743        impl fmt::Display for TestInteger {
744            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
745                self.0.fmt_integer(f)
746            }
747        }
748        assert_eq!(
749            TestInteger::from(format_bandwidth(Bandwidth::new(0, 0))).to_string(),
750            "0bps"
751        );
752        assert_eq!(
753            TestInteger::from(format_bandwidth(Bandwidth::new(0, 1))).to_string(),
754            "1bps"
755        );
756        assert_eq!(
757            TestInteger::from(format_bandwidth(Bandwidth::new(0, 15))).to_string(),
758            "15bps"
759        );
760        assert_eq!(
761            TestInteger::from(format_bandwidth(Bandwidth::new(0, 51_000))).to_string(),
762            "51kbps"
763        );
764        assert_eq!(
765            TestInteger::from(format_bandwidth(Bandwidth::new(0, 32_000_000))).to_string(),
766            "32Mbps"
767        );
768        assert_eq!(
769            TestInteger::from(format_bandwidth(Bandwidth::new(0, 79_000_000))).to_string(),
770            "79Mbps"
771        );
772        assert_eq!(
773            TestInteger::from(format_bandwidth(Bandwidth::new(0, 100_000_000))).to_string(),
774            "100Mbps"
775        );
776        assert_eq!(
777            TestInteger::from(format_bandwidth(Bandwidth::new(0, 150_000_000))).to_string(),
778            "150Mbps"
779        );
780        assert_eq!(
781            TestInteger::from(format_bandwidth(Bandwidth::new(0, 410_000_000))).to_string(),
782            "410Mbps"
783        );
784        assert_eq!(
785            TestInteger::from(format_bandwidth(Bandwidth::new(1, 0))).to_string(),
786            "1Gbps"
787        );
788        assert_eq!(
789            TestInteger::from(format_bandwidth(Bandwidth::new(4, 500_000_000))).to_string(),
790            "4Gbps 500Mbps"
791        );
792        assert_eq!(
793            TestInteger::from(format_bandwidth(Bandwidth::new(9420, 0))).to_string(),
794            "9Tbps 420Gbps"
795        );
796    }
797
798    #[test]
799    fn test_formatted_bandwidth_decimal() {
800        struct TestDecimal(FormattedBandwidth);
801        impl From<FormattedBandwidth> for TestDecimal {
802            fn from(fb: FormattedBandwidth) -> Self {
803                TestDecimal(fb)
804            }
805        }
806        impl fmt::Display for TestDecimal {
807            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
808                self.0.fmt_decimal(f)
809            }
810        }
811        assert_eq!(
812            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 0))).to_string(),
813            "0bps"
814        );
815        assert_eq!(
816            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 1))).to_string(),
817            "1bps"
818        );
819        assert_eq!(
820            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 15))).to_string(),
821            "15bps"
822        );
823        assert_eq!(
824            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 51_200))).to_string(),
825            "51.2kbps"
826        );
827        assert_eq!(
828            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 32_300_400))).to_string(),
829            "32.3004Mbps"
830        );
831        assert_eq!(
832            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 79_000_050))).to_string(),
833            "79.00005Mbps"
834        );
835        assert_eq!(
836            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 100_060_007))).to_string(),
837            "100.060007Mbps"
838        );
839        assert_eq!(
840            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 150_000_000))).to_string(),
841            "150Mbps"
842        );
843        assert_eq!(
844            TestDecimal::from(format_bandwidth(Bandwidth::new(0, 410_008_900))).to_string(),
845            "410.0089Mbps"
846        );
847        assert_eq!(
848            TestDecimal::from(format_bandwidth(Bandwidth::new(1, 0))).to_string(),
849            "1Gbps"
850        );
851        assert_eq!(
852            TestDecimal::from(format_bandwidth(Bandwidth::new(4, 500_000_000))).to_string(),
853            "4.5Gbps"
854        );
855        assert_eq!(
856            TestDecimal::from(format_bandwidth(Bandwidth::new(8700, 32_000_000))).to_string(),
857            "8.700032Tbps"
858        );
859        assert_eq!(
860            "9.42Tbps",
861            TestDecimal::from(format_bandwidth(Bandwidth::new(9420, 0))).to_string(),
862        );
863    }
864}