use std::fmt;
use std::marker::PhantomData;
use kanonak_canonical::{canonical_decimal, canonical_integer};
use serde::de::{self, Deserializer, Visitor};
use serde::{Deserialize, Serialize, Serializer};
use crate::{outside_bounds, within_double_precision, xsd_number_form, CodecError};
pub trait IntegerDatatype {
const LOCAL_NAME: &'static str;
}
macro_rules! integer_datatype {
($name:ident, $local:literal) => {
#[doc = concat!("`xsd:", $local, "`, for [`Integer`].")]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct $name;
impl IntegerDatatype for $name {
const LOCAL_NAME: &'static str = $local;
}
};
}
integer_datatype!(XsdInteger, "integer");
integer_datatype!(NonNegativeInteger, "nonNegativeInteger");
integer_datatype!(PositiveInteger, "positiveInteger");
integer_datatype!(NonPositiveInteger, "nonPositiveInteger");
integer_datatype!(NegativeInteger, "negativeInteger");
fn refusal(why: impl fmt::Display) -> CodecError {
CodecError::Malformed(format!("{} [json-number-form]", why))
}
fn integer_of_lexical(datatype: &str, lexical: &str) -> Result<String, CodecError> {
canonical_integer(lexical)
.map_err(|_| refusal(format!("{:?} is not an xsd:{} lexical form", lexical, datatype)))
}
fn safe_whole(digits: String) -> Option<String> {
Some(digits).filter(|d| within_double_precision(d))
}
fn whole_f64(v: f64) -> Option<String> {
if v.fract() != 0.0 {
return None;
}
canonical_integer(&format!("{:.0}", v)).ok().and_then(safe_whole)
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Integer<D: IntegerDatatype> {
digits: String,
datatype: PhantomData<D>,
}
impl<D: IntegerDatatype> Integer<D> {
pub fn parse(lexical: &str) -> Result<Self, CodecError> {
Self::from_digits(integer_of_lexical(D::LOCAL_NAME, lexical)?)
}
fn from_digits(digits: String) -> Result<Self, CodecError> {
let (_, min, max) = xsd_number_form(D::LOCAL_NAME)
.expect("every IntegerDatatype names a datatype in the codec's table");
if outside_bounds(&digits, min, max) {
return Err(refusal(format!("{} is outside xsd:{}'s range", digits, D::LOCAL_NAME)));
}
Ok(Integer {
digits,
datatype: PhantomData,
})
}
pub fn as_str(&self) -> &str {
&self.digits
}
}
impl<D: IntegerDatatype> fmt::Display for Integer<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.digits)
}
}
impl<D: IntegerDatatype> Serialize for Integer<D> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.digits)
}
}
impl<'de, D: IntegerDatatype> Deserialize<'de> for Integer<D> {
fn deserialize<De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error> {
deserializer.deserialize_any(WholeVisitor(D::LOCAL_NAME, Integer::<D>::from_digits))
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Long(pub i64);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct UnsignedLong(pub u64);
impl Long {
pub fn parse(lexical: &str) -> Result<Self, CodecError> {
Self::from_digits(integer_of_lexical("long", lexical)?)
}
fn from_digits(digits: String) -> Result<Self, CodecError> {
digits
.parse::<i64>()
.map(Long)
.map_err(|_| refusal(format!("{} is outside xsd:long's range", digits)))
}
}
impl UnsignedLong {
pub fn parse(lexical: &str) -> Result<Self, CodecError> {
Self::from_digits(integer_of_lexical("unsignedLong", lexical)?)
}
fn from_digits(digits: String) -> Result<Self, CodecError> {
digits
.parse::<u64>()
.map(UnsignedLong)
.map_err(|_| refusal(format!("{} is outside xsd:unsignedLong's range", digits)))
}
}
impl fmt::Display for Long {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for UnsignedLong {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Serialize for Long {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0.to_string())
}
}
impl Serialize for UnsignedLong {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> Deserialize<'de> for Long {
fn deserialize<De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error> {
deserializer.deserialize_any(WholeVisitor("long", Long::from_digits))
}
}
impl<'de> Deserialize<'de> for UnsignedLong {
fn deserialize<De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error> {
deserializer.deserialize_any(WholeVisitor("unsignedLong", UnsignedLong::from_digits))
}
}
struct WholeVisitor<T, F: FnOnce(String) -> Result<T, CodecError>>(&'static str, F);
impl<'de, T, F: FnOnce(String) -> Result<T, CodecError>> Visitor<'de> for WholeVisitor<T, F> {
type Value = T;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"an xsd:{} as a string of its digits, or a whole number within ±(2^53 − 1) [json-number-form]",
self.0
)
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<T, E> {
integer_of_lexical(self.0, v)
.and_then(self.1)
.map_err(E::custom)
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<T, E> {
let digits = safe_whole(v.to_string()).ok_or_else(|| E::custom(past_double(self.0, v)))?;
(self.1)(digits).map_err(E::custom)
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<T, E> {
let digits = safe_whole(v.to_string()).ok_or_else(|| E::custom(past_double(self.0, v)))?;
(self.1)(digits).map_err(E::custom)
}
fn visit_f64<E: de::Error>(self, v: f64) -> Result<T, E> {
let digits = whole_f64(v).ok_or_else(|| {
E::custom(refusal(format!(
"an xsd:{} is read from a string, or from a whole JSON number within ±(2^53 − 1); got the number {}",
self.0, v
)))
})?;
(self.1)(digits).map_err(E::custom)
}
}
fn past_double(datatype: &str, v: impl fmt::Display) -> CodecError {
refusal(format!(
"the number {} is past ±(2^53 − 1), where a JSON reader may have rounded it, so an xsd:{} past it is read from a string",
v, datatype
))
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Decimal(String);
impl Decimal {
pub fn parse(lexical: &str) -> Result<Self, CodecError> {
canonical_decimal(lexical)
.map(Decimal)
.map_err(|_| refusal(format!("{:?} is not an xsd:decimal lexical form", lexical)))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for Decimal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl Serialize for Decimal {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0)
}
}
impl<'de> Deserialize<'de> for Decimal {
fn deserialize<De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error> {
struct DecimalVisitor;
impl<'de> Visitor<'de> for DecimalVisitor {
type Value = Decimal;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("an xsd:decimal as a string of its digits, never a number, which may have been rounded [json-number-form]")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Decimal, E> {
Decimal::parse(v).map_err(E::custom)
}
}
deserializer.deserialize_any(DecimalVisitor)
}
}