double_int/
lib.rs

1//! The double-int format represents an integer that can be stored in an IEEE 754 double-precision
2//! number without loss of precision.
3//!
4//! This crate has been designed for use with OpenAPI tooling that wish to support integer-based
5//! `format: double-int` fields. [See docs in the OpenAPI format registry.][reg_double_int]
6//!
7//! # Examples
8//!
9//! ```
10//! # use double_int::DoubleInt;
11//! #[derive(Debug, serde::Deserialize)]
12//! struct Config {
13//!     count: DoubleInt,
14//! }
15//!
16//! let config = toml::from_str::<Config>(r#"
17//!     count = 42
18//! "#).unwrap();
19//! assert_eq!(config.count, 42);
20//!
21//! let config = toml::from_str::<Config>(r#"
22//!     count = -42
23//! "#).unwrap();
24//! assert_eq!(config.count, -42);
25//!
26//! // count is outside the bounds of a double-int (> 2^53 in this case)
27//! // (this would usually be accepted by an i64)
28//! let config = toml::from_str::<Config>(r#"
29//!     count = 36028797018963968
30//! "#).unwrap_err();
31//! ```
32//!
33//! [reg_double_int]: https://spec.openapis.org/registry/format/double-int
34
35#![no_std]
36#![deny(rust_2018_idioms, nonstandard_style, future_incompatible)]
37#![cfg_attr(docsrs, feature(doc_auto_cfg))]
38
39use serde_core::{de, Deserialize, Deserializer, Serialize, Serializer};
40
41/// Type that only deserializes from the `true` boolean value.
42///
43/// # Examples
44///
45/// ```
46/// assert_eq!(
47///     serde_json::from_str::<double_int::DoubleInt>("42").unwrap(),
48///     42,
49/// );
50///
51/// serde_json::from_str::<double_int::DoubleInt>("4.2").unwrap_err();
52/// serde_json::from_str::<double_int::DoubleInt>("36028797018963968").unwrap_err();
53/// ```
54#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
55pub struct DoubleInt(i64);
56
57impl DoubleInt {
58    const MIN: i128 = -(2_i128.pow(53)) + 1;
59    const MAX: i128 = 2_i128.pow(53) - 1;
60    const UMAX: u128 = 2_u128.pow(53) - 1;
61
62    /// Returns value as a standard type.
63    pub const fn as_i64(self) -> i64 {
64        self.0
65    }
66}
67
68macro_rules! from_impl {
69    ($ty:ty) => {
70        impl From<$ty> for DoubleInt {
71            fn from(val: $ty) -> Self {
72                Self(val as i64)
73            }
74        }
75    };
76}
77
78from_impl!(u8);
79from_impl!(u16);
80from_impl!(u32);
81from_impl!(i8);
82from_impl!(i16);
83from_impl!(i32);
84
85macro_rules! infallible_eq_impls {
86    ($ty:ty) => {
87        impl PartialEq<$ty> for DoubleInt {
88            fn eq(&self, val: &$ty) -> bool {
89                self.0 == *val as i64
90            }
91        }
92    };
93}
94
95infallible_eq_impls!(u8);
96infallible_eq_impls!(u16);
97infallible_eq_impls!(u32);
98infallible_eq_impls!(i8);
99infallible_eq_impls!(i16);
100infallible_eq_impls!(i32);
101
102impl PartialEq<u64> for DoubleInt {
103    fn eq(&self, val: &u64) -> bool {
104        match *val as u128 {
105            // self cannot be larger than UMAX so val is not equal
106            DoubleInt::UMAX.. => false,
107
108            // all remaining u64s would be representable by i64
109            // just cast and check equality
110            _ => self.0 == *val as i64,
111        }
112    }
113}
114
115impl PartialEq<u128> for DoubleInt {
116    fn eq(&self, val: &u128) -> bool {
117        match val {
118            // self cannot be larger than UMAX so val is not equal
119            DoubleInt::UMAX.. => false,
120
121            // all remaining u64s would be representable by i64
122            // just cast and check equality
123            _ => self.0 == *val as i64,
124        }
125    }
126}
127
128impl PartialEq<i64> for DoubleInt {
129    fn eq(&self, val: &i64) -> bool {
130        self.0 == *val
131    }
132}
133
134impl PartialEq<i128> for DoubleInt {
135    fn eq(&self, val: &i128) -> bool {
136        match val {
137            // self cannot be larger than UMAX so val is not equal
138            DoubleInt::MAX.. => false,
139
140            // all remaining u64s would be representable by i64
141            // just cast and check equality
142            _ => self.0 == *val as i64,
143        }
144    }
145}
146
147impl<'de> Deserialize<'de> for DoubleInt {
148    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
149        match i64::deserialize(deserializer) {
150            Err(err) => Err(err),
151
152            Ok(val) if (val as i128) < DoubleInt::MIN => Err(de::Error::invalid_value(
153                de::Unexpected::Signed(val),
154                &"integer larger than -9007199254740991 / -(2^53) + 1",
155            )),
156
157            Ok(val) if (val as i128) > DoubleInt::MAX => Err(de::Error::invalid_value(
158                de::Unexpected::Signed(val),
159                &"integer smaller than 9007199254740991 / (2^53) - 1",
160            )),
161
162            Ok(val) => Ok(DoubleInt(val)),
163        }
164    }
165}
166
167impl Serialize for DoubleInt {
168    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
169        serializer.serialize_i64(self.0)
170    }
171}
172
173// #[cfg(test)]
174// mod tests {
175//     use super::*;
176
177//     #[derive(Debug, Deserialize, PartialEq)]
178//     struct Tru {
179//         foo: DoubleInt,
180//     }
181
182//     #[test]
183//     fn de_true() {
184//         assert_eq!(
185//             Tru { foo: DoubleInt },
186//             serde_json::from_str::<Tru>(r#"{"foo": true}"#).unwrap(),
187//         );
188
189//         serde_json::from_str::<Tru>(r#"{"foo": false}"#).unwrap_err();
190//         serde_json::from_str::<Tru>(r#"{"foo": 42}"#).unwrap_err();
191//     }
192
193//     #[derive(Debug, Deserialize, PartialEq)]
194//     struct Fal {
195//         foo: False,
196//     }
197
198//     #[test]
199//     fn de_false() {
200//         assert_eq!(
201//             Fal { foo: False },
202//             serde_json::from_str::<Fal>(r#"{"foo": false}"#).unwrap(),
203//         );
204
205//         serde_json::from_str::<Fal>(r#"{"foo": true}"#).unwrap_err();
206//         serde_json::from_str::<Fal>(r#"{"foo": 42}"#).unwrap_err();
207//     }
208
209//     #[test]
210//     fn ser() {
211//         assert_eq!("true", serde_json::to_string(&DoubleInt).unwrap());
212//         assert_eq!("false", serde_json::to_string(&False).unwrap());
213//     }
214
215//     #[test]
216//     fn as_bool() {
217//         assert!(DoubleInt.as_bool());
218//         assert!(!False.as_bool());
219//     }
220
221//     #[test]
222//     fn from() {
223//         assert!(bool::from(DoubleInt));
224//         assert!(!bool::from(False));
225//     }
226
227//     #[test]
228//     fn eq() {
229//         assert_eq!(DoubleInt, DoubleInt);
230//         assert_eq!(DoubleInt, true);
231//         assert_eq!(true, DoubleInt);
232//         assert_eq!(False, False);
233//         assert_eq!(False, false);
234//         assert_eq!(false, False);
235
236//         assert_ne!(DoubleInt, False);
237//         assert_ne!(DoubleInt, false);
238//         assert_ne!(False, DoubleInt);
239//         assert_ne!(false, DoubleInt);
240
241//         assert_ne!(False, DoubleInt);
242//         assert_ne!(False, true);
243//         assert_ne!(DoubleInt, False);
244//         assert_ne!(true, False);
245//     }
246
247//     #[test]
248//     fn formatting() {
249//         let _ = format_args!("{:?}", DoubleInt);
250//         let _ = format_args!("{:?}", False);
251//     }
252
253//     #[test]
254//     fn other_implementations() {
255//         #![allow(clippy::default_constructed_unit_structs)]
256
257//         assert_eq!(DoubleInt.clone(), DoubleInt);
258//         assert_eq!(False.clone(), False);
259
260//         assert_eq!(DoubleInt::default(), DoubleInt);
261//         assert_eq!(False::default(), False);
262//     }
263// }