use crate::value::Value;
use crate::value::error::Error;
use crate::value::parse;
use fixed::types::I48F16;
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct Number(I48F16);
impl core::fmt::Display for Number {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(&self.0.to_string())
}
}
impl From<usize> for Number {
fn from(value: usize) -> Self {
Self(I48F16::from_num(value))
}
}
impl TryFrom<&[u8]> for Number {
type Error = Error;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
I48F16::from_str(parse::str(value)?)
.map_err(|e| Error::Number(e.to_string()))
.map(Self)
}
}
impl TryFrom<Number> for usize {
type Error = ();
fn try_from(value: Number) -> Result<Self, Self::Error> {
value.0.checked_to_num().ok_or(())
}
}
impl TryFrom<Number> for isize {
type Error = ();
fn try_from(value: Number) -> Result<Self, Self::Error> {
value.0.checked_to_num().ok_or(())
}
}
impl TryFrom<Number> for u32 {
type Error = ();
fn try_from(value: Number) -> Result<Self, Self::Error> {
value.0.checked_to_num().ok_or(())
}
}
impl From<Number> for Value {
fn from(number: Number) -> Self {
Value::from(number.to_string())
}
}
impl core::ops::Add for Number {
type Output = Number;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0.saturating_add(rhs.0))
}
}
impl core::ops::Sub for Number {
type Output = Number;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0.saturating_sub(rhs.0))
}
}
impl core::ops::Mul for Number {
type Output = Number;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0.checked_mul(rhs.0).unwrap_or_default())
}
}
impl core::ops::Div for Number {
type Output = Number;
fn div(self, rhs: Self) -> Self::Output {
Self(self.0.checked_div(rhs.0).unwrap_or_default())
}
}
impl core::ops::Rem for Number {
type Output = Number;
fn rem(self, rhs: Self) -> Self::Output {
Self(self.0.checked_rem(rhs.0).unwrap_or_default())
}
}
impl Number {
pub(crate) fn abs(self) -> Number {
Self(self.0.checked_abs().unwrap_or_default())
}
pub(crate) fn ceil(self) -> Number {
Self(self.0.checked_ceil().unwrap_or_default())
}
pub(crate) fn floor(self) -> Number {
Self(self.0.checked_floor().unwrap_or_default())
}
pub(crate) fn round(self) -> Number {
Self(self.0.checked_round().unwrap_or_default())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_add() {
assert_eq!((number("1") + number("2")).to_string(), "3");
assert_eq!((number("1") + number("-2")).to_string(), "-1");
assert_eq!((number("1") + number("2.01")).to_string(), "3.01");
}
#[test]
fn test_sub() {
assert_eq!((number("1") - number("1")).to_string(), "0");
assert_eq!((number("-1") - number("-10")).to_string(), "9");
assert_eq!((number("1") - number("1.1")).to_string(), "-0.1");
}
#[test]
fn test_div() {
assert_eq!((number("1") / number("0")).to_string(), "0");
assert_eq!((number("0") / number("1")).to_string(), "0");
assert_eq!((number("0") / number("0")).to_string(), "0");
}
#[track_caller]
fn number(s: &str) -> Number {
Number::try_from(s.as_bytes()).unwrap()
}
}