Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

SJ

Copyright (C) 2019-2025  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2019-2025".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Number

use {
    core::{
        fmt::{self, Display, Formatter},
        ptr,
    },
    crate::{Error, Result},
};

mod impls;
mod number_writer;
mod tests;

macro_rules! to_le_bytes { ($n: ident) => { $n.to_le_bytes() }}
macro_rules! from_le_bytes { ($ty: ty, $bytes: expr) => { <$ty>::from_le_bytes($bytes) }}

#[cfg(feature="std")]
#[doc(cfg(feature="std"))]
pub (crate) use self::number_writer::*;

/// # Max integer digits
///
/// _For internal use only._
#[cfg(feature="std")]
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))]
pub (crate) const MAX_INT_DIGITS: usize = 40;

/// # Max integer digits
///
/// _For internal use only._
///
/// This is redundant. But, on this big machine, some large buffer like this is fine.
#[cfg(feature="std")]
#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64")))]
pub (crate) const MAX_INT_DIGITS: usize = core::mem::size_of::<usize>() * 3;

#[cfg(feature="std")]
#[test]
fn tests() {
    use alloc::string::ToString;

    assert!(MAX_INT_DIGITS >= u128::max_value().to_string().len());
    assert!(MAX_INT_DIGITS >= i128::min_value().to_string().len());

    assert!(MAX_INT_DIGITS >= usize::max_value().to_string().len());
    assert!(MAX_INT_DIGITS >= isize::min_value().to_string().len());
}

const DATA_SIZE: usize = size_of::<u128>();

/// # Number
///
/// You rarely use this directly, but [`Json`][enum:Json].
///
/// ## Notes
///
/// -   Internally, this struct uses an enum to store values of different types (such as `i64`, `f64`...). Since different number types cannot
///     be compared directly, [`PartialOrd`][core::cmp/PartialOrd] is not implemented.
///
/// -   [`Hash`][core::hash/Hash] is not implemented. Because [`f32`][f32] and [`f64`][f64] don't implement it.
///
/// [enum:Json]: enum.Json.html
///
/// [f32]: https://doc.rust-lang.org/std/primitive.f32.html
/// [f64]: https://doc.rust-lang.org/std/primitive.f64.html
/// [core::cmp/PartialOrd]: https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html
/// [core::hash/Hash]: https://doc.rust-lang.org/core/hash/trait.Hash.html
#[derive(Debug, Clone, Copy)]
pub struct Number {
    kind: Kind,
    data: [u8; DATA_SIZE],
}

impl Number {

    /// # U64
    fn as_u64(&self) -> u64 {
        unsafe {
            from_le_bytes!(u64, *ptr::from_ref(&self.data[..size_of::<u64>()]).cast())
        }
    }

    /// # I64
    fn as_i64(&self) -> i64 {
        unsafe {
            from_le_bytes!(i64, *ptr::from_ref(&self.data[..size_of::<i64>()]).cast())
        }
    }

    /// # U128
    fn as_u128(&self) -> u128 {
        unsafe {
            from_le_bytes!(u128, *ptr::from_ref(&self.data[..size_of::<u128>()]).cast())
        }
    }

    /// # I128
    fn as_i128(&self) -> i128 {
        unsafe {
            from_le_bytes!(i128, *ptr::from_ref(&self.data[..size_of::<i128>()]).cast())
        }
    }

    /// # F64
    fn as_f64(&self) -> f64 {
        unsafe {
            from_le_bytes!(f64, *ptr::from_ref(&self.data[..size_of::<f64>()]).cast())
        }
    }

    /// # F32
    fn as_f32(&self) -> f32 {
        unsafe {
            from_le_bytes!(f32, *ptr::from_ref(&self.data[..size_of::<f32>()]).cast())
        }
    }

}

/// # Inner value of `Number`
///
/// This helps prevent misuses, because JSON numbers have no type.
///
/// If we expose this enum, the user may incorrectly use `match` or `if let` on a **parsed** `Json::Number(...)`. For example, they may expect
/// some ID field to be a `u64`, while the parser could have parsed it as a `u8`.
#[derive(Debug, Clone, Copy)]
enum Kind {
    I64, U64,
    I128, U128,
    F32, F64,
}

impl Display for Number {

    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self.kind {
            Kind::I64 => self.as_i64().fmt(f),
            Kind::U64 => self.as_u64().fmt(f),

            Kind::I128 => self.as_i128().fmt(f),
            Kind::U128 => self.as_u128().fmt(f),

            Kind::F32 => self.as_f32().fmt(f),
            Kind::F64 => self.as_f64().fmt(f),
        }
    }

}

macro_rules! impl_from_primitives_for_number { ($(($primitive: ty, $variant: tt, $target: ty)),+$(,)?) => {
    $(
        impl From<&$primitive> for Number {

            fn from(p: &$primitive) -> Self {
                let mut result = Self {
                    kind: Kind::$variant,
                    data: [u8::MIN; DATA_SIZE],
                };

                let p = <$target>::from(*p);
                result.data[..size_of::<$target>()].copy_from_slice(&to_le_bytes!(p));

                result
            }

        }

        impl From<$primitive> for Number {

            fn from(p: $primitive) -> Self {
                Self::from(&p)
            }

        }
    )+
}}

impl_from_primitives_for_number!(
    (i8, I64, i64), (i16, I64, i64), (i32, I64, i64), (i64, I64, i64), (i128, I128, i128),
    (u8, U64, u64), (u16, U64, u64), (u32, U64, u64), (u64, U64, u64), (u128, U128, u128),
    (f32, F32, f32), (f64, F64, f64),
);

#[cfg(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))]
macro_rules! impl_from_usize_isize_for_number { ($(($primitive: ty, $variant64: tt, $target: ty)),+$(,)?) => {
    $(
        impl From<&$primitive> for Number {

            #[cfg(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))]
            fn from(n: &$primitive) -> Self {
                let mut result = Self {
                    kind: Kind::$variant64,
                    data: [u8::MIN; DATA_SIZE],
                };

                let n = *n as $target;
                result.data[..size_of::<$target>()].copy_from_slice(&to_le_bytes!(n));

                result
            }

        }

        impl From<$primitive> for Number {

            fn from(n: $primitive) -> Self {
                Self::from(&n)
            }

        }
    )+
}}

#[cfg(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))]
impl_from_usize_isize_for_number!(
    (usize, U64, u64),
    (isize, I64, i64),
);

macro_rules! impl_try_from_number_for_integers { ($($ty: ty,)+) => {
    $(
        impl TryFrom<&Number> for $ty {

            type Error = Error;

            fn try_from(n: &Number) -> Result<Self> {
                match n.kind {
                    Kind::U64 => {
                        let n = n.as_u64();
                        Self::try_from(n).map_err(|_| err!("Cannot convert {n} into {ty}", ty=stringify!($ty)))
                    },
                    Kind::I64 => {
                        let n = n.as_i64();
                        Self::try_from(n).map_err(|_| err!("Cannot convert {n} into {ty}", ty=stringify!($ty)))
                    },
                    Kind::U128 => {
                        let n = n.as_u128();
                        Self::try_from(n).map_err(|_| err!("Cannot convert {n} into {ty}", ty=stringify!($ty)))
                    },
                    Kind::I128 => {
                        let n = n.as_i128();
                        Self::try_from(n).map_err(|_| err!("Cannot convert {n} into {ty}", ty=stringify!($ty)))
                    },
                    Kind::F32 | Kind::F64 => Err(e!("Not an integer")),
                }
            }

        }

        impl TryFrom<Number> for $ty {

            type Error = Error;

            fn try_from(n: Number) -> Result<Self> {
                Self::try_from(&n)
            }

        }
    )+
}}

impl_try_from_number_for_integers!(
    i8, i16, i32, i64, i128, isize,
    u8, u16, u32, u64, u128, usize,
);

impl TryFrom<&Number> for f32 {

    type Error = Error;

    fn try_from(n: &Number) -> Result<Self> {
        match n.kind {
            Kind::U64 => {
                let n = n.as_u64();
                u16::try_from(n).map(|n| Self::from(n)).map_err(|_| err!("Cannot convert {n} into f32"))
            },
            Kind::I64 => {
                let n = n.as_i64();
                i16::try_from(n).map(|n| Self::from(n)).map_err(|_| err!("Cannot convert {n} into f32"))
            },
            Kind::U128 => {
                let n = n.as_u128();
                u16::try_from(n).map(|n| Self::from(n)).map_err(|_| err!("Cannot convert {n} into f32"))
            },
            Kind::I128 => {
                let n = n.as_i128();
                i16::try_from(n).map(|n| Self::from(n)).map_err(|_| err!("Cannot convert {n} into f32"))
            },
            Kind::F32 => Ok(n.as_f32()),
            Kind::F64 => Err(e!("Cannot convert f64 into f32")),
        }
    }

}

impl TryFrom<Number> for f32 {

    type Error = Error;

    fn try_from(n: Number) -> Result<Self> {
        Self::try_from(&n)
    }

}

impl TryFrom<&Number> for f64 {

    type Error = Error;

    fn try_from(n: &Number) -> Result<Self> {
        match n.kind {
            Kind::U64 => {
                let n = n.as_u64();
                u32::try_from(n).map(|n| Self::from(n)).map_err(|_| err!("Cannot convert {n} into f64"))
            },
            Kind::I64 => {
                let n = n.as_i64();
                i32::try_from(n).map(|n| Self::from(n)).map_err(|_| err!("Cannot convert {n} into f64"))
            },
            Kind::U128 => {
                let n = n.as_u128();
                u32::try_from(n).map(|n| Self::from(n)).map_err(|_| err!("Cannot convert {n} into f64"))
            },
            Kind::I128 => {
                let n = n.as_i128();
                i32::try_from(n).map(|n| Self::from(n)).map_err(|_| err!("Cannot convert {n} into f64"))
            },
            Kind::F32 => Ok(n.as_f32().into()),
            Kind::F64 => Ok(n.as_f64()),
        }
    }

}

impl TryFrom<Number> for f64 {

    type Error = Error;

    fn try_from(n: Number) -> Result<Self> {
        Self::try_from(&n)
    }

}