1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use core::fmt::{Binary, Display, Formatter, LowerExp, LowerHex, Octal, Pointer, Result, UpperExp, UpperHex};
use core::ops::Bound;

use crate::{Domain, GenericRange};

macro_rules! format_range_with {
    ($($t:ident),+) => {
        $(
            /// Non-Debug formatting uses interval notation and formats the bound values
            /// according to the given formatting arguments.
            ///
            /// # Note
            /// Unless disabled by using the `-` formatting argument, a whitespace will be printed
            /// after the bound separator.
            /// Because there is currently no use for `-`, it will have no effect on the underlying
            /// bound values. This might change if requested or core/std makes use of it.
            ///
            ///  # Examples
            /// ```
            /// use ranges::GenericRange;
            ///
            /// assert_eq!(format!("{}", GenericRange::from(1..2)), "{1}");
            /// assert_eq!(format!("{}", GenericRange::from(1..=2)), "[1, 2]");
            /// assert_eq!(format!("{}", GenericRange::from(42..)), "[42, 2147483647]");
            /// assert_eq!(format!("{:03}", GenericRange::from(1..2)), "{001}");
            /// assert_eq!(format!("{:02X}", GenericRange::from(1..=10)), "[01, 0A]");
            /// assert_eq!(format!("{:-#}", GenericRange::from(42..=100)), "[42,100]");
            /// ```
            impl<T: $t + Domain> $t for GenericRange<T> {
                fn fmt(&self, f: &mut Formatter<'_>) -> Result {
                    if self.is_singleton() {
                        f.write_str("{")?;

                        match (&self.start, &self.end) {
                            (Bound::Included(v), Bound::Excluded(_))
                            | (Bound::Excluded(_), Bound::Included(v))
                            | (Bound::Included(v), Bound::Included(_)) => {
                                (*v).fmt(f)
                            },
                            (Bound::Excluded(v), Bound::Excluded(_)) => {
                                let succ = (*v).successor().expect("open singleton has no successor");
                                (succ).fmt(f)
                            },
                            (Bound::Unbounded, _) |(_, Bound::Unbounded) => unreachable!("singleton is unbounded"),
                        }?;

                        f.write_str("}")
                    } else {
                        match &self.start {
                            Bound::Included(v) => {
                                f.write_str("[")?;
                                (*v).fmt(f)
                            }
                            Bound::Excluded(v) => {
                                f.write_str("(")?;
                                (*v).fmt(f)
                            }
                            Bound::Unbounded => f.write_str("(-inf"),
                        }?;

                        f.write_str(",")?;

                        if !f.sign_minus() {
                            f.write_str(" ")?;
                        }

                        match &self.end {
                            Bound::Included(v) => {
                                (*v).fmt(f)?;
                                f.write_str("]")
                            }
                            Bound::Excluded(v) => {
                                (*v).fmt(f)?;
                                f.write_str(")")
                            }
                            Bound::Unbounded => f.write_str("+inf)"),
                        }
                    }
                }
            }
        )*
    }
}

format_range_with!(Binary, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex);

#[cfg(test)]
mod tests {
    use alloc::format;
    use core::ops::Bound;

    use crate::GenericRange;

    #[test]
    fn display() {
        assert_eq!(format!("{}", GenericRange::from(1..2)), "{1}");
        assert_eq!(format!("{}", GenericRange::from(1..=2)), "[1, 2]");
        assert_eq!(format!("{}", GenericRange::from(42..)), "[42, 2147483647]");
        assert_eq!(format!("{}", GenericRange::from(..2)), "[-2147483648, 2)");
        assert_eq!(format!("{}", GenericRange::from(..=2)), "[-2147483648, 2]");
        let generic: GenericRange<usize> = GenericRange::from(..);
        assert_eq!(format!("{}", generic), "[0, 18446744073709551615]");
        assert_eq!(
            format!("{}", GenericRange::from((Bound::Excluded(1), Bound::Excluded(2)))),
            "(1, 2)"
        );
        assert_eq!(
            format!("{}", GenericRange::from((Bound::Excluded(3), Bound::Excluded(5)))),
            "{4}"
        );
        assert_eq!(
            format!("{}", GenericRange::from((Bound::Excluded(1), Bound::Included(2)))),
            "{2}"
        );
        assert_eq!(
            format!("{}", GenericRange::from((Bound::Excluded(1), Bound::Unbounded))),
            "(1, 2147483647]"
        );

        assert_eq!(format!("{:03}", GenericRange::from(1..2)), "{001}");
        assert_eq!(format!("{:02X}", GenericRange::from(1..=10)), "[01, 0A]");
        assert_eq!(format!("{:-#}", GenericRange::from(42..=100)), "[42,100]");
    }
}