1use std::fmt;
9use std::marker::PhantomData;
10
11use kanonak_canonical::{canonical_decimal, canonical_integer};
12use serde::de::{self, Deserializer, Visitor};
13use serde::{Deserialize, Serialize, Serializer};
14
15use crate::{outside_bounds, within_double_precision, xsd_number_form, CodecError};
16
17pub trait IntegerDatatype {
21 const LOCAL_NAME: &'static str;
24}
25
26macro_rules! integer_datatype {
27 ($name:ident, $local:literal) => {
28 #[doc = concat!("`xsd:", $local, "`, for [`Integer`].")]
29 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
30 pub struct $name;
31
32 impl IntegerDatatype for $name {
33 const LOCAL_NAME: &'static str = $local;
34 }
35 };
36}
37
38integer_datatype!(XsdInteger, "integer");
39integer_datatype!(NonNegativeInteger, "nonNegativeInteger");
40integer_datatype!(PositiveInteger, "positiveInteger");
41integer_datatype!(NonPositiveInteger, "nonPositiveInteger");
42integer_datatype!(NegativeInteger, "negativeInteger");
43
44fn refusal(why: impl fmt::Display) -> CodecError {
45 CodecError::Malformed(format!("{} [json-number-form]", why))
46}
47
48fn integer_of_lexical(datatype: &str, lexical: &str) -> Result<String, CodecError> {
49 canonical_integer(lexical)
50 .map_err(|_| refusal(format!("{:?} is not an xsd:{} lexical form", lexical, datatype)))
51}
52
53fn safe_whole(digits: String) -> Option<String> {
55 Some(digits).filter(|d| within_double_precision(d))
56}
57
58fn whole_f64(v: f64) -> Option<String> {
59 if v.fract() != 0.0 {
60 return None;
61 }
62 canonical_integer(&format!("{:.0}", v)).ok().and_then(safe_whole)
63}
64
65#[derive(Clone, PartialEq, Eq, Hash, Debug)]
70pub struct Integer<D: IntegerDatatype> {
71 digits: String,
72 datatype: PhantomData<D>,
73}
74
75impl<D: IntegerDatatype> Integer<D> {
76 pub fn parse(lexical: &str) -> Result<Self, CodecError> {
78 Self::from_digits(integer_of_lexical(D::LOCAL_NAME, lexical)?)
79 }
80
81 fn from_digits(digits: String) -> Result<Self, CodecError> {
82 let (_, min, max) = xsd_number_form(D::LOCAL_NAME)
83 .expect("every IntegerDatatype names a datatype in the codec's table");
84 if outside_bounds(&digits, min, max) {
85 return Err(refusal(format!("{} is outside xsd:{}'s range", digits, D::LOCAL_NAME)));
86 }
87 Ok(Integer {
88 digits,
89 datatype: PhantomData,
90 })
91 }
92
93 pub fn as_str(&self) -> &str {
95 &self.digits
96 }
97}
98
99impl<D: IntegerDatatype> fmt::Display for Integer<D> {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 f.write_str(&self.digits)
102 }
103}
104
105impl<D: IntegerDatatype> Serialize for Integer<D> {
106 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
107 serializer.serialize_str(&self.digits)
108 }
109}
110
111impl<'de, D: IntegerDatatype> Deserialize<'de> for Integer<D> {
112 fn deserialize<De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error> {
113 deserializer.deserialize_any(WholeVisitor(D::LOCAL_NAME, Integer::<D>::from_digits))
114 }
115}
116
117#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
120pub struct Long(pub i64);
121
122#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
125pub struct UnsignedLong(pub u64);
126
127impl Long {
128 pub fn parse(lexical: &str) -> Result<Self, CodecError> {
130 Self::from_digits(integer_of_lexical("long", lexical)?)
131 }
132
133 fn from_digits(digits: String) -> Result<Self, CodecError> {
134 digits
135 .parse::<i64>()
136 .map(Long)
137 .map_err(|_| refusal(format!("{} is outside xsd:long's range", digits)))
138 }
139}
140
141impl UnsignedLong {
142 pub fn parse(lexical: &str) -> Result<Self, CodecError> {
144 Self::from_digits(integer_of_lexical("unsignedLong", lexical)?)
145 }
146
147 fn from_digits(digits: String) -> Result<Self, CodecError> {
148 digits
149 .parse::<u64>()
150 .map(UnsignedLong)
151 .map_err(|_| refusal(format!("{} is outside xsd:unsignedLong's range", digits)))
152 }
153}
154
155impl fmt::Display for Long {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 write!(f, "{}", self.0)
158 }
159}
160
161impl fmt::Display for UnsignedLong {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 write!(f, "{}", self.0)
164 }
165}
166
167impl Serialize for Long {
168 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
169 serializer.serialize_str(&self.0.to_string())
170 }
171}
172
173impl Serialize for UnsignedLong {
174 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
175 serializer.serialize_str(&self.0.to_string())
176 }
177}
178
179impl<'de> Deserialize<'de> for Long {
180 fn deserialize<De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error> {
181 deserializer.deserialize_any(WholeVisitor("long", Long::from_digits))
182 }
183}
184
185impl<'de> Deserialize<'de> for UnsignedLong {
186 fn deserialize<De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error> {
187 deserializer.deserialize_any(WholeVisitor("unsignedLong", UnsignedLong::from_digits))
188 }
189}
190
191struct WholeVisitor<T, F: FnOnce(String) -> Result<T, CodecError>>(&'static str, F);
195
196impl<'de, T, F: FnOnce(String) -> Result<T, CodecError>> Visitor<'de> for WholeVisitor<T, F> {
197 type Value = T;
198
199 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 write!(
201 f,
202 "an xsd:{} as a string of its digits, or a whole number within ±(2^53 − 1) [json-number-form]",
203 self.0
204 )
205 }
206
207 fn visit_str<E: de::Error>(self, v: &str) -> Result<T, E> {
208 integer_of_lexical(self.0, v)
209 .and_then(self.1)
210 .map_err(E::custom)
211 }
212
213 fn visit_i64<E: de::Error>(self, v: i64) -> Result<T, E> {
214 let digits = safe_whole(v.to_string()).ok_or_else(|| E::custom(past_double(self.0, v)))?;
215 (self.1)(digits).map_err(E::custom)
216 }
217
218 fn visit_u64<E: de::Error>(self, v: u64) -> Result<T, E> {
219 let digits = safe_whole(v.to_string()).ok_or_else(|| E::custom(past_double(self.0, v)))?;
220 (self.1)(digits).map_err(E::custom)
221 }
222
223 fn visit_f64<E: de::Error>(self, v: f64) -> Result<T, E> {
224 let digits = whole_f64(v).ok_or_else(|| {
225 E::custom(refusal(format!(
226 "an xsd:{} is read from a string, or from a whole JSON number within ±(2^53 − 1); got the number {}",
227 self.0, v
228 )))
229 })?;
230 (self.1)(digits).map_err(E::custom)
231 }
232}
233
234fn past_double(datatype: &str, v: impl fmt::Display) -> CodecError {
235 refusal(format!(
236 "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",
237 v, datatype
238 ))
239}
240
241#[derive(Clone, PartialEq, Eq, Hash, Debug)]
245pub struct Decimal(String);
246
247impl Decimal {
248 pub fn parse(lexical: &str) -> Result<Self, CodecError> {
250 canonical_decimal(lexical)
251 .map(Decimal)
252 .map_err(|_| refusal(format!("{:?} is not an xsd:decimal lexical form", lexical)))
253 }
254
255 pub fn as_str(&self) -> &str {
257 &self.0
258 }
259}
260
261impl fmt::Display for Decimal {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 f.write_str(&self.0)
264 }
265}
266
267impl Serialize for Decimal {
268 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
269 serializer.serialize_str(&self.0)
270 }
271}
272
273impl<'de> Deserialize<'de> for Decimal {
274 fn deserialize<De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error> {
275 struct DecimalVisitor;
276
277 impl<'de> Visitor<'de> for DecimalVisitor {
278 type Value = Decimal;
279
280 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.write_str("an xsd:decimal as a string of its digits, never a number, which may have been rounded [json-number-form]")
282 }
283
284 fn visit_str<E: de::Error>(self, v: &str) -> Result<Decimal, E> {
285 Decimal::parse(v).map_err(E::custom)
286 }
287 }
288
289 deserializer.deserialize_any(DecimalVisitor)
290 }
291}