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