1use crate::{Boolean, Decimal, Double, Float};
2use std::fmt;
3use std::num::ParseIntError;
4use std::str::FromStr;
5
6#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
10#[repr(transparent)]
11pub struct Integer {
12 value: i64,
13}
14
15impl Integer {
16 pub const MAX: Self = Self { value: i64::MAX };
17 pub const MIN: Self = Self { value: i64::MIN };
18
19 #[inline]
20 #[must_use]
21 pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
22 Self {
23 value: i64::from_be_bytes(bytes),
24 }
25 }
26
27 #[inline]
28 #[must_use]
29 pub fn to_be_bytes(self) -> [u8; 8] {
30 self.value.to_be_bytes()
31 }
32
33 #[inline]
37 #[must_use]
38 pub fn checked_add(self, rhs: impl Into<Self>) -> Option<Self> {
39 Some(Self {
40 value: self.value.checked_add(rhs.into().value)?,
41 })
42 }
43
44 #[inline]
48 #[must_use]
49 pub fn checked_sub(self, rhs: impl Into<Self>) -> Option<Self> {
50 Some(Self {
51 value: self.value.checked_sub(rhs.into().value)?,
52 })
53 }
54
55 #[inline]
59 #[must_use]
60 pub fn checked_mul(self, rhs: impl Into<Self>) -> Option<Self> {
61 Some(Self {
62 value: self.value.checked_mul(rhs.into().value)?,
63 })
64 }
65
66 #[inline]
70 #[must_use]
71 pub fn checked_div(self, rhs: impl Into<Self>) -> Option<Self> {
72 Some(Self {
73 value: self.value.checked_div(rhs.into().value)?,
74 })
75 }
76
77 #[inline]
81 #[must_use]
82 pub fn checked_rem(self, rhs: impl Into<Self>) -> Option<Self> {
83 Some(Self {
84 value: self.value.checked_rem(rhs.into().value)?,
85 })
86 }
87
88 #[inline]
92 #[must_use]
93 pub fn checked_rem_euclid(self, rhs: impl Into<Self>) -> Option<Self> {
94 Some(Self {
95 value: self.value.checked_rem_euclid(rhs.into().value)?,
96 })
97 }
98
99 #[inline]
103 #[must_use]
104 pub fn checked_neg(self) -> Option<Self> {
105 Some(Self {
106 value: self.value.checked_neg()?,
107 })
108 }
109
110 #[inline]
114 #[must_use]
115 pub fn checked_abs(self) -> Option<Self> {
116 Some(Self {
117 value: self.value.checked_abs()?,
118 })
119 }
120
121 #[inline]
122 #[must_use]
123 pub const fn is_negative(self) -> bool {
124 self.value < 0
125 }
126
127 #[inline]
128 #[must_use]
129 pub const fn is_positive(self) -> bool {
130 self.value > 0
131 }
132
133 #[inline]
135 #[must_use]
136 pub fn is_identical_with(self, other: Self) -> bool {
137 self == other
138 }
139}
140
141impl From<bool> for Integer {
142 #[inline]
143 fn from(value: bool) -> Self {
144 Self {
145 value: value.into(),
146 }
147 }
148}
149
150impl From<i8> for Integer {
151 #[inline]
152 fn from(value: i8) -> Self {
153 Self {
154 value: value.into(),
155 }
156 }
157}
158
159impl From<i16> for Integer {
160 #[inline]
161 fn from(value: i16) -> Self {
162 Self {
163 value: value.into(),
164 }
165 }
166}
167
168impl From<i32> for Integer {
169 #[inline]
170 fn from(value: i32) -> Self {
171 Self {
172 value: value.into(),
173 }
174 }
175}
176
177impl From<i64> for Integer {
178 #[inline]
179 fn from(value: i64) -> Self {
180 Self { value }
181 }
182}
183
184impl From<u8> for Integer {
185 #[inline]
186 fn from(value: u8) -> Self {
187 Self {
188 value: value.into(),
189 }
190 }
191}
192
193impl From<u16> for Integer {
194 #[inline]
195 fn from(value: u16) -> Self {
196 Self {
197 value: value.into(),
198 }
199 }
200}
201
202impl From<u32> for Integer {
203 #[inline]
204 fn from(value: u32) -> Self {
205 Self {
206 value: value.into(),
207 }
208 }
209}
210
211impl From<Boolean> for Integer {
212 #[inline]
213 fn from(value: Boolean) -> Self {
214 bool::from(value).into()
215 }
216}
217
218impl From<Integer> for i64 {
219 #[inline]
220 fn from(value: Integer) -> Self {
221 value.value
222 }
223}
224
225impl FromStr for Integer {
226 type Err = ParseIntError;
227
228 #[inline]
229 fn from_str(input: &str) -> Result<Self, Self::Err> {
230 Ok(i64::from_str(input)?.into())
231 }
232}
233
234impl fmt::Display for Integer {
235 #[inline]
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 self.value.fmt(f)
238 }
239}
240
241impl TryFrom<Float> for Integer {
242 type Error = TooLargeForIntegerError;
243
244 #[inline]
245 fn try_from(value: Float) -> Result<Self, Self::Error> {
246 Decimal::try_from(value)
247 .map_err(|_| TooLargeForIntegerError)?
248 .try_into()
249 }
250}
251
252impl TryFrom<Double> for Integer {
253 type Error = TooLargeForIntegerError;
254
255 #[inline]
256 fn try_from(value: Double) -> Result<Self, Self::Error> {
257 Decimal::try_from(value)
258 .map_err(|_| TooLargeForIntegerError)?
259 .try_into()
260 }
261}
262
263#[derive(Debug, Clone, Copy, thiserror::Error)]
267#[error("Value too large for xsd:integer internal representation")]
268pub struct TooLargeForIntegerError;
269
270#[cfg(test)]
271#[allow(clippy::panic_in_result_fn)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn from_str() -> Result<(), ParseIntError> {
277 assert_eq!(Integer::from_str("0")?.to_string(), "0");
278 assert_eq!(Integer::from_str("-0")?.to_string(), "0");
279 assert_eq!(Integer::from_str("123")?.to_string(), "123");
280 assert_eq!(Integer::from_str("-123")?.to_string(), "-123");
281 Integer::from_str("123456789123456789123456789123456789123456789").unwrap_err();
282 Ok(())
283 }
284
285 #[test]
286 fn from_float() -> Result<(), ParseIntError> {
287 assert_eq!(
288 Integer::try_from(Float::from(0.)).ok(),
289 Some(Integer::from_str("0")?)
290 );
291 assert_eq!(
292 Integer::try_from(Float::from(-0.)).ok(),
293 Some(Integer::from_str("0")?)
294 );
295 assert_eq!(
296 Integer::try_from(Float::from(-123.1)).ok(),
297 Some(Integer::from_str("-123")?)
298 );
299 Integer::try_from(Float::from(f32::NAN)).unwrap_err();
300 Integer::try_from(Float::from(f32::INFINITY)).unwrap_err();
301 Integer::try_from(Float::from(f32::NEG_INFINITY)).unwrap_err();
302 Integer::try_from(Float::from(f32::MIN)).unwrap_err();
303 Integer::try_from(Float::from(f32::MAX)).unwrap_err();
304 assert!(
305 Integer::try_from(Float::from(1_672_507_300_000.))
306 .unwrap()
307 .checked_sub(Integer::from_str("1672507300000")?)
308 .unwrap()
309 .checked_abs()
310 .unwrap()
311 < Integer::from(1_000_000)
312 );
313 Ok(())
314 }
315
316 #[test]
317 fn from_double() -> Result<(), ParseIntError> {
318 assert_eq!(
319 Integer::try_from(Double::from(0.0)).ok(),
320 Some(Integer::from_str("0")?)
321 );
322 assert_eq!(
323 Integer::try_from(Double::from(-0.0)).ok(),
324 Some(Integer::from_str("0")?)
325 );
326 assert_eq!(
327 Integer::try_from(Double::from(-123.1)).ok(),
328 Some(Integer::from_str("-123")?)
329 );
330 assert!(
331 Integer::try_from(Double::from(1_672_507_300_000.))
332 .unwrap()
333 .checked_sub(Integer::from_str("1672507300000").unwrap())
334 .unwrap()
335 .checked_abs()
336 .unwrap()
337 < Integer::from(10)
338 );
339 Integer::try_from(Double::from(f64::NAN)).unwrap_err();
340 Integer::try_from(Double::from(f64::INFINITY)).unwrap_err();
341 Integer::try_from(Double::from(f64::NEG_INFINITY)).unwrap_err();
342 Integer::try_from(Double::from(f64::MIN)).unwrap_err();
343 Integer::try_from(Double::from(f64::MAX)).unwrap_err();
344 Ok(())
345 }
346
347 #[test]
348 fn from_decimal() -> Result<(), ParseIntError> {
349 assert_eq!(
350 Integer::try_from(Decimal::from(0)).ok(),
351 Some(Integer::from_str("0")?)
352 );
353 assert_eq!(
354 Integer::try_from(Decimal::from_str("-123.1").unwrap()).ok(),
355 Some(Integer::from_str("-123")?)
356 );
357 Integer::try_from(Decimal::MIN).unwrap_err();
358 Integer::try_from(Decimal::MAX).unwrap_err();
359 Ok(())
360 }
361
362 #[test]
363 fn add() {
364 assert_eq!(
365 Integer::MIN.checked_add(1),
366 Some(Integer::from(i64::MIN + 1))
367 );
368 assert_eq!(Integer::MAX.checked_add(1), None);
369 }
370
371 #[test]
372 fn sub() {
373 assert_eq!(Integer::MIN.checked_sub(1), None);
374 assert_eq!(
375 Integer::MAX.checked_sub(1),
376 Some(Integer::from(i64::MAX - 1))
377 );
378 }
379
380 #[test]
381 fn mul() {
382 assert_eq!(Integer::MIN.checked_mul(2), None);
383 assert_eq!(Integer::MAX.checked_mul(2), None);
384 }
385
386 #[test]
387 fn div() {
388 assert_eq!(Integer::from(1).checked_div(0), None);
389 }
390
391 #[test]
392 fn rem() {
393 assert_eq!(Integer::from(10).checked_rem(3), Some(Integer::from(1)));
394 assert_eq!(Integer::from(6).checked_rem(-2), Some(Integer::from(0)));
395 assert_eq!(Integer::from(1).checked_rem(0), None);
396 }
397}