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
use num_traits::cast::ToPrimitive;

#[derive(Clone, Debug)]
pub enum BigIntOrI64 {
    Int(i64),
    BigInt(num_bigint::BigInt),
}

impl PartialEq for BigIntOrI64 {
    fn eq(&self, other: &Self) -> bool {
        use BigIntOrI64::*;
        match (&self, &other) {
            (Int(i), Int(j)) => i == j,
            (Int(i), BigInt(b)) | (BigInt(b), Int(i)) => b == &num_bigint::BigInt::from(*i),
            (BigInt(a), BigInt(b)) => a == b,
        }
    }
}

impl Eq for BigIntOrI64 {}

/// A value holding JavaScript
/// [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) type
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BigInt {
    pub(crate) inner: BigIntOrI64,
}

impl BigInt {
    /// Return `Some` if value fits into `i64` and `None` otherwise
    pub fn as_i64(&self) -> Option<i64> {
        match &self.inner {
            BigIntOrI64::Int(int) => Some(*int),
            BigIntOrI64::BigInt(bigint) => bigint.to_i64(),
        }
    }
    /// Convert value into `num_bigint::BigInt`
    pub fn into_bigint(self) -> num_bigint::BigInt {
        match self.inner {
            BigIntOrI64::Int(int) => int.into(),
            BigIntOrI64::BigInt(bigint) => bigint,
        }
    }
}

impl std::fmt::Display for BigInt {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.inner {
            BigIntOrI64::Int(i) => write!(f, "{}", i),
            BigIntOrI64::BigInt(ref i) => write!(f, "{}", i),
        }
    }
}

impl From<i64> for BigInt {
    fn from(int: i64) -> Self {
        BigInt {
            inner: BigIntOrI64::Int(int),
        }
    }
}

impl From<num_bigint::BigInt> for BigInt {
    fn from(bigint: num_bigint::BigInt) -> Self {
        BigInt {
            inner: BigIntOrI64::BigInt(bigint),
        }
    }
}

#[cfg(test)]
mod tests {
    #[allow(unused_imports)]
    use super::*;

    #[test]
    fn test_bigint_as_i64() {
        let value = BigInt {
            inner: BigIntOrI64::Int(1234i64),
        };
        assert_eq!(value.as_i64(), Some(1234i64));
    }

    #[test]
    fn test_bigint_as_i64_overflow() {
        let value = BigInt {
            inner: BigIntOrI64::BigInt(num_bigint::BigInt::from(std::i128::MAX)),
        };
        assert_eq!(value.as_i64(), None);
    }

    #[test]
    fn test_bigint_into_bigint() {
        for i in vec![
            0 as i128,
            std::i64::MAX as i128,
            std::i64::MIN as i128,
            std::i128::MAX,
            std::i128::MIN,
        ] {
            let value = BigInt {
                inner: BigIntOrI64::BigInt(num_bigint::BigInt::from(i)),
            };
            assert_eq!(value.into_bigint(), num_bigint::BigInt::from(i));
        }
    }
}