use std::marker::PhantomData;
use serde::de::{self, Deserialize, DeserializeOwned, Deserializer};
use super::{Kinds, Raw, Shape, Unfit};
pub trait Whole: Copy + DeserializeOwned {
const PAST: &'static str;
const WIDE: bool;
fn fit(v: i128) -> Option<Self>;
fn fit_huge(v: u128) -> Option<Self>;
}
macro_rules! whole {
($($wide:literal: $($t:ty),*;)*) => {$($(
impl Whole for $t {
const WIDE: bool = $wide;
const PAST: &'static str = concat!("an integer outside ", stringify!($t));
fn fit(v: i128) -> Option<$t> {
<$t>::try_from(v).ok()
}
fn fit_huge(v: u128) -> Option<$t> {
<$t>::try_from(v).ok()
}
}
)*)*};
}
whole!(false: i8, i16, i32, i64, u8, u16, u32, u64; true: i128, u128;);
pub struct Int<T>(PhantomData<fn() -> T>);
impl<T> Clone for Int<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Int<T> {}
impl<'de, T: Whole> Kinds<'de> for Int<T> {
type Value = T;
const EXPECTED: &'static str = "an integer";
fn read<D: Deserializer<'de>>(self, d: D) -> Result<T, D::Error> {
match T::WIDE {
true => self.number(Raw::deserialize(d)?.get()),
false => d.deserialize_any(Shape(self)),
}
}
fn binary<D: Deserializer<'de>>(self, d: D) -> Result<T, D::Error> {
T::deserialize(d)
}
fn int<E: de::Error>(self, v: i128) -> Result<T, E> {
T::fit(v).ok_or_else(|| Unfit::range(T::PAST).raise())
}
fn huge<E: de::Error>(self, v: u128) -> Result<T, E> {
T::fit_huge(v).ok_or_else(|| Unfit::range(T::PAST).raise())
}
fn float<E: de::Error>(self, f: f64) -> Result<T, E> {
if f != f.trunc() {
return Err(Self::wrong());
}
match f.abs() < 1.7e38 {
#[allow(clippy::cast_possible_truncation)]
true => self.int(f as i128),
false => Err(Unfit::range(T::PAST).raise()),
}
}
}
impl<T: Whole> Int<T> {
fn number<E: de::Error>(self, text: &str) -> Result<T, E> {
if let Ok(v) = text.parse::<i128>() {
return self.int(v);
}
if let Ok(v) = text.parse::<u128>() {
return self.huge(v);
}
match text.parse::<f64>() {
Ok(f) if text.starts_with(|c: char| c == '-' || c.is_ascii_digit()) => self.float(f),
_ => Err(Self::wrong()),
}
}
}
pub trait Real: Copy + DeserializeOwned {
fn make(f: f64) -> Self;
}
impl Real for f64 {
fn make(f: f64) -> f64 {
f
}
}
impl Real for f32 {
#[allow(clippy::cast_possible_truncation)]
fn make(f: f64) -> f32 {
f as f32
}
}
pub struct Float<T>(PhantomData<fn() -> T>);
impl<T> Clone for Float<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Float<T> {}
impl<'de, T: Real> Kinds<'de> for Float<T> {
type Value = T;
const EXPECTED: &'static str = "a number";
fn binary<D: Deserializer<'de>>(self, d: D) -> Result<T, D::Error> {
T::deserialize(d)
}
#[allow(clippy::cast_precision_loss)]
fn int<E: de::Error>(self, v: i128) -> Result<T, E> {
Ok(T::make(v as f64))
}
fn float<E: de::Error>(self, v: f64) -> Result<T, E> {
Ok(T::make(v))
}
}
pub fn int<T: Whole>() -> Shape<Int<T>> {
Shape(Int(PhantomData))
}
pub fn float<T: Real>() -> Shape<Float<T>> {
Shape(Float(PhantomData))
}