magnus 0.4.4

High level Ruby bindings for Rust.
Documentation
use std::{convert::TryFrom, fmt, ops::Deref};

use rb_sys::{rb_ll2inum, rb_to_int, rb_ull2inum, ruby_special_consts, ruby_value_type, VALUE};

use crate::{
    debug_assert_value,
    error::{protect, Error},
    exception,
    r_bignum::RBignum,
    try_convert::TryConvert,
    value::{private, Fixnum, NonZeroValue, ReprValue, Value},
};

pub(crate) enum IntegerType {
    Fixnum(Fixnum),
    Bignum(RBignum),
}

/// A type wrapping either a [`Fixnum`] or a [`RBignum`].
///
/// All [`Value`] methods should be available on this type through [`Deref`],
/// but some may be missed by this documentation.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Integer(NonZeroValue);

impl Integer {
    /// Return `Some(Integer)` if `val` is an `Integer`, `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(Integer::from_value(eval("0").unwrap()).is_some());
    /// assert!(Integer::from_value(eval("9223372036854775807").unwrap()).is_some());
    /// // not an int
    /// assert!(Integer::from_value(eval("1.23").unwrap()).is_none());
    /// ```
    #[inline]
    pub fn from_value(val: Value) -> Option<Self> {
        unsafe {
            if val.as_rb_value() & ruby_special_consts::RUBY_FIXNUM_FLAG as VALUE != 0 {
                return Some(Self(NonZeroValue::new_unchecked(val)));
            }
            debug_assert_value!(val);
            (val.rb_type() == ruby_value_type::RUBY_T_BIGNUM)
                .then(|| Self(NonZeroValue::new_unchecked(val)))
        }
    }

    #[inline]
    pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
        Self(NonZeroValue::new_unchecked(Value::new(val)))
    }

    pub(crate) fn integer_type(self) -> IntegerType {
        unsafe {
            if self.as_rb_value() & ruby_special_consts::RUBY_FIXNUM_FLAG as VALUE != 0 {
                IntegerType::Fixnum(Fixnum::from_rb_value_unchecked(self.as_rb_value()))
            } else {
                IntegerType::Bignum(RBignum::from_rb_value_unchecked(self.as_rb_value()))
            }
        }
    }

    /// Create a new `Integer` from an `i64.`
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let res: bool = eval!("i == 0", i = Integer::from_i64(0)).unwrap();
    /// assert!(res);
    /// let res: bool = eval!("i == 4611686018427387904", i = Integer::from_i64(4611686018427387904)).unwrap();
    /// assert!(res);
    /// let res: bool = eval!("i == -4611686018427387905", i = Integer::from_i64(-4611686018427387905)).unwrap();
    /// assert!(res);
    /// ```
    #[inline]
    pub fn from_i64(n: i64) -> Self {
        unsafe {
            Self::from_rb_value_unchecked(
                Fixnum::from_i64_impl(n)
                    .map(|f| f.as_rb_value())
                    .unwrap_or_else(|| rb_ll2inum(n)),
            )
        }
    }

    /// Create a new `Integer` from a `u64.`
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let res: bool = eval!("i == 0", i = Integer::from_u64(0)).unwrap();
    /// assert!(res);
    /// let res: bool = eval!("i == 4611686018427387904", i = Integer::from_u64(4611686018427387904)).unwrap();
    /// assert!(res);
    /// ```
    #[inline]
    pub fn from_u64(n: u64) -> Self {
        unsafe {
            Self::from_rb_value_unchecked(
                Fixnum::from_i64_impl(i64::try_from(n).unwrap_or(i64::MAX))
                    .map(|f| f.as_rb_value())
                    .unwrap_or_else(|| rb_ull2inum(n)),
            )
        }
    }

    /// Convert `self` to an `i8`. Returns `Err` if `self` is out of range for
    /// `i8`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("127").unwrap().to_i8().unwrap(), 127);
    /// assert!(eval::<Integer>("128").unwrap().to_i8().is_err());
    /// assert_eq!(eval::<Integer>("-128").unwrap().to_i8().unwrap(), -128);
    /// assert!(eval::<Integer>("-129").unwrap().to_i8().is_err());
    /// ```
    #[inline]
    pub fn to_i8(self) -> Result<i8, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => fix.to_i8(),
            IntegerType::Bignum(_) => Err(Error::new(
                exception::range_error(),
                "bignum too big to convert into `i8`",
            )),
        }
    }

    /// Convert `self` to an `i16`. Returns `Err` if `self` is out of range for
    /// `i16`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("32767").unwrap().to_i16().unwrap(), 32767);
    /// assert!(eval::<Integer>("32768").unwrap().to_i16().is_err());
    /// assert_eq!(eval::<Integer>("-32768").unwrap().to_i16().unwrap(), -32768);
    /// assert!(eval::<Integer>("-32769").unwrap().to_i16().is_err());
    /// ```
    #[inline]
    pub fn to_i16(self) -> Result<i16, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => fix.to_i16(),
            IntegerType::Bignum(_) => Err(Error::new(
                exception::range_error(),
                "bignum too big to convert into `i16`",
            )),
        }
    }

    /// Convert `self` to an `i32`. Returns `Err` if `self` is out of range for
    /// `i32`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("2147483647").unwrap().to_i32().unwrap(), 2147483647);
    /// assert!(eval::<Integer>("2147483648").unwrap().to_i32().is_err());
    /// assert_eq!(eval::<Integer>("-2147483648").unwrap().to_i32().unwrap(), -2147483648);
    /// assert!(eval::<Integer>("-2147483649").unwrap().to_i32().is_err());
    /// ```
    #[inline]
    pub fn to_i32(self) -> Result<i32, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => fix.to_i32(),
            IntegerType::Bignum(big) => big.to_i32(),
        }
    }

    /// Convert `self` to an `i64`. Returns `Err` if `self` is out of range for
    /// `i64`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("4611686018427387903").unwrap().to_i64().unwrap(), 4611686018427387903);
    /// assert_eq!(eval::<Integer>("-4611686018427387904").unwrap().to_i64().unwrap(), -4611686018427387904);
    /// assert!(eval::<Integer>("9223372036854775808").unwrap().to_i64().is_err());
    /// assert!(eval::<Integer>("-9223372036854775809").unwrap().to_i64().is_err());
    /// ```
    #[inline]
    pub fn to_i64(self) -> Result<i64, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => Ok(fix.to_i64()),
            IntegerType::Bignum(big) => big.to_i64(),
        }
    }

    /// Convert `self` to an `isize`. Returns `Err` if `self` is out of range
    /// for `isize`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("4611686018427387903").unwrap().to_isize().unwrap(), 4611686018427387903);
    /// assert_eq!(eval::<Integer>("-4611686018427387904").unwrap().to_isize().unwrap(), -4611686018427387904);
    /// ```
    #[inline]
    pub fn to_isize(self) -> Result<isize, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => Ok(fix.to_isize()),
            IntegerType::Bignum(big) => big.to_isize(),
        }
    }

    /// Convert `self` to a `u8`. Returns `Err` if `self` is negative or out of
    /// range for `u8`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("255").unwrap().to_u8().unwrap(), 255);
    /// assert!(eval::<Integer>("256").unwrap().to_u8().is_err());
    /// assert!(eval::<Integer>("-1").unwrap().to_u8().is_err());
    /// ```
    #[inline]
    pub fn to_u8(self) -> Result<u8, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => fix.to_u8(),
            IntegerType::Bignum(_) => Err(Error::new(
                exception::range_error(),
                "bignum too big to convert into `u8`",
            )),
        }
    }

    /// Convert `self` to a `u16`. Returns `Err` if `self` is negative or out
    /// of range for `u16`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("65535").unwrap().to_u16().unwrap(), 65535);
    /// assert!(eval::<Integer>("65536").unwrap().to_u16().is_err());
    /// assert!(eval::<Integer>("-1").unwrap().to_u16().is_err());
    /// ```
    #[inline]
    pub fn to_u16(self) -> Result<u16, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => fix.to_u16(),
            IntegerType::Bignum(_) => Err(Error::new(
                exception::range_error(),
                "bignum too big to convert into `u16`",
            )),
        }
    }

    /// Convert `self` to a `u32`. Returns `Err` if `self` is negative or out
    /// of range for `u32`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("4294967295").unwrap().to_u32().unwrap(), 4294967295);
    /// assert!(eval::<Integer>("4294967296").unwrap().to_u32().is_err());
    /// assert!(eval::<Integer>("-1").unwrap().to_u32().is_err());
    /// ```
    #[inline]
    pub fn to_u32(self) -> Result<u32, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => fix.to_u32(),
            IntegerType::Bignum(big) => big.to_u32(),
        }
    }

    /// Convert `self` to a `u64`. Returns `Err` if `self` is negative or out
    /// of range for `u64`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("4611686018427387903").unwrap().to_u64().unwrap(), 4611686018427387903);
    /// assert!(eval::<Integer>("-1").unwrap().to_u64().is_err());
    /// assert!(eval::<Integer>("18446744073709551616").unwrap().to_u64().is_err());
    /// ```
    #[inline]
    pub fn to_u64(self) -> Result<u64, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => fix.to_u64(),
            IntegerType::Bignum(big) => big.to_u64(),
        }
    }

    /// Convert `self` to a `usize`. Returns `Err` if `self` is negative or out
    /// of range for `usize`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Integer};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Integer>("4611686018427387903").unwrap().to_usize().unwrap(), 4611686018427387903);
    /// assert!(eval::<Integer>("-1").unwrap().to_usize().is_err());
    /// ```
    #[inline]
    pub fn to_usize(self) -> Result<usize, Error> {
        match self.integer_type() {
            IntegerType::Fixnum(fix) => fix.to_usize(),
            IntegerType::Bignum(big) => big.to_usize(),
        }
    }
}

impl Deref for Integer {
    type Target = Value;

    fn deref(&self) -> &Self::Target {
        self.0.get_ref()
    }
}

impl fmt::Display for Integer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", unsafe { self.to_s_infallible() })
    }
}

impl fmt::Debug for Integer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.inspect())
    }
}

impl From<Integer> for Value {
    fn from(val: Integer) -> Self {
        *val
    }
}

unsafe impl private::ReprValue for Integer {
    fn to_value(self) -> Value {
        *self
    }

    unsafe fn from_value_unchecked(val: Value) -> Self {
        Self(NonZeroValue::new_unchecked(val))
    }
}

impl ReprValue for Integer {}

impl TryConvert for Integer {
    fn try_convert(val: Value) -> Result<Self, Error> {
        match Self::from_value(val) {
            Some(i) => Ok(i),
            None => protect(|| {
                debug_assert_value!(val);
                unsafe { Self::from_rb_value_unchecked(rb_to_int(val.as_rb_value())) }
            }),
        }
    }
}