1use std::cmp::Ordering;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use std::str::FromStr;
5
6use serde::de::{self, Visitor};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9use crate::{Error, Result};
10
11#[derive(Clone, Copy)]
16pub struct Number {
17 repr: Repr,
18}
19
20#[derive(Clone, Copy)]
21enum Repr {
22 Signed(i128),
23 Unsigned(u128),
24 Float(f64),
25}
26
27impl Number {
28 pub(crate) const fn signed(value: i128) -> Self {
29 Self {
30 repr: Repr::Signed(value),
31 }
32 }
33
34 pub(crate) const fn unsigned(value: u128) -> Self {
35 Self {
36 repr: Repr::Unsigned(value),
37 }
38 }
39
40 pub(crate) const fn float(value: f64) -> Self {
41 Self {
42 repr: Repr::Float(value),
43 }
44 }
45
46 #[must_use]
48 pub fn is_i64(&self) -> bool {
49 self.as_i64().is_some()
50 }
51
52 #[must_use]
54 pub fn is_u64(&self) -> bool {
55 self.as_u64().is_some()
56 }
57
58 #[must_use]
60 pub const fn is_f64(&self) -> bool {
61 matches!(self.repr, Repr::Float(_))
62 }
63
64 #[must_use]
66 pub fn is_i128(&self) -> bool {
67 self.as_i128().is_some()
68 }
69
70 #[must_use]
72 pub fn is_u128(&self) -> bool {
73 self.as_u128().is_some()
74 }
75
76 #[must_use]
78 pub fn as_i64(&self) -> Option<i64> {
79 self.as_i128().and_then(|value| i64::try_from(value).ok())
80 }
81
82 #[must_use]
84 pub fn as_u64(&self) -> Option<u64> {
85 self.as_u128().and_then(|value| u64::try_from(value).ok())
86 }
87
88 #[must_use]
90 pub fn as_i128(&self) -> Option<i128> {
91 match self.repr {
92 Repr::Signed(value) => Some(value),
93 Repr::Unsigned(value) => i128::try_from(value).ok(),
94 Repr::Float(_) => None,
95 }
96 }
97
98 #[must_use]
100 pub fn as_u128(&self) -> Option<u128> {
101 match self.repr {
102 Repr::Signed(value) => u128::try_from(value).ok(),
103 Repr::Unsigned(value) => Some(value),
104 Repr::Float(_) => None,
105 }
106 }
107
108 #[must_use]
110 pub fn as_f64(&self) -> Option<f64> {
111 Some(match self.repr {
112 Repr::Signed(value) => value as f64,
113 Repr::Unsigned(value) => value as f64,
114 Repr::Float(value) => value,
115 })
116 }
117
118 #[must_use]
120 pub fn is_nan(&self) -> bool {
121 matches!(self.repr, Repr::Float(value) if value.is_nan())
122 }
123
124 #[must_use]
126 pub fn is_infinite(&self) -> bool {
127 matches!(self.repr, Repr::Float(value) if value.is_infinite())
128 }
129
130 #[must_use]
132 pub fn is_finite(&self) -> bool {
133 !matches!(self.repr, Repr::Float(value) if !value.is_finite())
134 }
135}
136
137impl PartialEq for Number {
138 fn eq(&self, other: &Self) -> bool {
139 match (self.repr, other.repr) {
140 (Repr::Signed(left), Repr::Signed(right)) => left == right,
141 (Repr::Unsigned(left), Repr::Unsigned(right)) => left == right,
142 (Repr::Signed(left), Repr::Unsigned(right))
143 | (Repr::Unsigned(right), Repr::Signed(left)) => {
144 u128::try_from(left).is_ok_and(|left| left == right)
145 }
146 (Repr::Float(left), Repr::Float(right)) => left == right,
147 _ => false,
148 }
149 }
150}
151
152impl PartialOrd for Number {
153 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
154 match (self.repr, other.repr) {
155 (Repr::Signed(left), Repr::Signed(right)) => left.partial_cmp(&right),
156 (Repr::Unsigned(left), Repr::Unsigned(right)) => left.partial_cmp(&right),
157 (Repr::Signed(left), Repr::Unsigned(right)) => {
158 if left < 0 {
159 Some(Ordering::Less)
160 } else {
161 (left as u128).partial_cmp(&right)
162 }
163 }
164 (Repr::Unsigned(left), Repr::Signed(right)) => {
165 if right < 0 {
166 Some(Ordering::Greater)
167 } else {
168 left.partial_cmp(&(right as u128))
169 }
170 }
171 (Repr::Float(left), Repr::Float(right)) => left.partial_cmp(&right),
172 (left, right) => repr_as_f64(left).partial_cmp(&repr_as_f64(right)),
173 }
174 }
175}
176
177fn repr_as_f64(value: Repr) -> f64 {
178 match value {
179 Repr::Signed(value) => value as f64,
180 Repr::Unsigned(value) => value as f64,
181 Repr::Float(value) => value,
182 }
183}
184
185impl Hash for Number {
186 fn hash<H: Hasher>(&self, state: &mut H) {
187 match self.repr {
188 Repr::Signed(value) if value >= 0 => {
189 0_u8.hash(state);
190 (value as u128).hash(state);
191 }
192 Repr::Signed(value) => {
193 1_u8.hash(state);
194 value.hash(state);
195 }
196 Repr::Unsigned(value) => {
197 0_u8.hash(state);
198 value.hash(state);
199 }
200 Repr::Float(value) => {
201 2_u8.hash(state);
202 let bits = if value == 0.0 { 0 } else { value.to_bits() };
203 bits.hash(state);
204 }
205 }
206 }
207}
208
209impl fmt::Display for Number {
210 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self.repr {
212 Repr::Signed(value) => value.fmt(formatter),
213 Repr::Unsigned(value) => value.fmt(formatter),
214 Repr::Float(value) if value.is_nan() => formatter.write_str(".nan"),
215 Repr::Float(value) if value == f64::INFINITY => formatter.write_str(".inf"),
216 Repr::Float(value) if value == f64::NEG_INFINITY => formatter.write_str("-.inf"),
217 Repr::Float(value) => {
218 let text = value.to_string();
219 formatter.write_str(&text)?;
220 if !text.contains(['.', 'e', 'E']) {
221 formatter.write_str(".0")?;
222 }
223 Ok(())
224 }
225 }
226 }
227}
228
229impl fmt::Debug for Number {
230 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
231 fmt::Display::fmt(self, formatter)
232 }
233}
234
235impl FromStr for Number {
236 type Err = Error;
237
238 fn from_str(value: &str) -> Result<Self> {
239 match crate::from_str::<super::Value>(value)? {
240 super::Value::Number(number) => Ok(number),
241 _ => Err(Error::message("expected a YAML number")),
242 }
243 }
244}
245
246macro_rules! from_signed {
247 ($($ty:ty),+ $(,)?) => {
248 $(
249 impl From<$ty> for Number {
250 fn from(value: $ty) -> Self {
251 Self::signed(value as i128)
252 }
253 }
254 )+
255 };
256}
257
258macro_rules! from_unsigned {
259 ($($ty:ty),+ $(,)?) => {
260 $(
261 impl From<$ty> for Number {
262 fn from(value: $ty) -> Self {
263 Self::unsigned(value as u128)
264 }
265 }
266 )+
267 };
268}
269
270from_signed!(i8, i16, i32, i64, i128, isize);
271from_unsigned!(u8, u16, u32, u64, u128, usize);
272
273impl From<f32> for Number {
274 fn from(value: f32) -> Self {
275 Self::float(value.into())
276 }
277}
278
279impl From<f64> for Number {
280 fn from(value: f64) -> Self {
281 Self::float(value)
282 }
283}
284
285impl Serialize for Number {
286 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
287 where
288 S: Serializer,
289 {
290 match self.repr {
291 Repr::Signed(value) => serializer.serialize_i128(value),
292 Repr::Unsigned(value) => serializer.serialize_u128(value),
293 Repr::Float(value) => serializer.serialize_f64(value),
294 }
295 }
296}
297
298impl<'de> Deserialize<'de> for Number {
299 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
300 where
301 D: Deserializer<'de>,
302 {
303 struct NumberVisitor;
304
305 impl<'de> Visitor<'de> for NumberVisitor {
306 type Value = Number;
307
308 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
309 formatter.write_str("a YAML number")
310 }
311
312 fn visit_i64<E>(self, value: i64) -> std::result::Result<Number, E> {
313 Ok(Number::from(value))
314 }
315
316 fn visit_i128<E>(self, value: i128) -> std::result::Result<Number, E> {
317 Ok(Number::from(value))
318 }
319
320 fn visit_u64<E>(self, value: u64) -> std::result::Result<Number, E> {
321 Ok(Number::from(value))
322 }
323
324 fn visit_u128<E>(self, value: u128) -> std::result::Result<Number, E> {
325 Ok(Number::from(value))
326 }
327
328 fn visit_f64<E>(self, value: f64) -> std::result::Result<Number, E> {
329 Ok(Number::from(value))
330 }
331 }
332
333 deserializer.deserialize_any(NumberVisitor)
334 }
335}
336
337impl<'de> de::IntoDeserializer<'de, Error> for Number {
338 type Deserializer = super::Value;
339
340 fn into_deserializer(self) -> Self::Deserializer {
341 super::Value::Number(self)
342 }
343}