lexpr/
number.rs

1//! Dynamically typed number type.
2
3use std::fmt::{self, Debug, Display};
4
5/// Represents an S-expression number, whether integer or floating point.
6#[derive(PartialEq, Clone)]
7pub struct Number {
8    n: N,
9}
10
11#[derive(Debug, PartialEq, Clone)]
12enum N {
13    PosInt(u64),
14    NegInt(i64),
15    Float(f64),
16}
17
18impl Number {
19    /// Returns true if the `Number` is an integer between `i64::MIN` and
20    /// `i64::MAX`.
21    ///
22    /// For any `Number` on which `is_i64` returns true, `as_i64` is
23    /// guaranteed to return the integer value.
24    ///
25    /// ```
26    /// # use lexpr::sexp;
27    /// #
28    /// let big = i64::max_value() as u64 + 10;
29    /// let v = sexp!(((a . 64) (b . ,big) (c . 256.0)));
30    ///
31    /// assert!(v["a"].is_i64());
32    ///
33    /// // Greater than i64::MAX.
34    /// assert!(!v["b"].is_i64());
35    ///
36    /// // Numbers with a decimal point are not considered integers.
37    /// assert!(!v["c"].is_i64());
38    /// ```
39    #[inline]
40    pub fn is_i64(&self) -> bool {
41        match self.n {
42            N::PosInt(v) => v <= i64::max_value() as u64,
43            N::NegInt(_) => true,
44            N::Float(_) => false,
45        }
46    }
47
48    /// Returns true if the `Number` is an integer between zero and `u64::MAX`.
49    ///
50    /// For any Number on which `is_u64` returns true, `as_u64` is guaranteed to
51    /// return the integer value.
52    ///
53    /// ```
54    /// # use lexpr::sexp;
55    /// #
56    /// let v = sexp!(((a . 64) (b . -64) (c . 256.0)));
57    ///
58    /// assert!(v["a"].is_u64());
59    ///
60    /// // Negative integer.
61    /// assert!(!v["b"].is_u64());
62    ///
63    /// // Numbers with a decimal point are not considered integers.
64    /// assert!(!v["c"].is_u64());
65    /// ```
66    #[inline]
67    pub fn is_u64(&self) -> bool {
68        match self.n {
69            N::PosInt(_) => true,
70            N::NegInt(_) | N::Float(_) => false,
71        }
72    }
73
74    /// Returns true if the `Number` can be represented by f64.
75    ///
76    /// For any Number on which `is_f64` returns true, `as_f64` is guaranteed to
77    /// return the floating point value.
78    ///
79    /// Currently this function returns true if and only if both `is_i64` and
80    /// `is_u64` return false but this is not a guarantee in the future.
81    ///
82    /// ```
83    /// # use lexpr::sexp;
84    /// #
85    /// let v = sexp!(((a . 256.0) (b . 64) (c . -64)));
86    /// assert!(v["a"].is_f64());
87    ///
88    /// // Integers.
89    /// assert!(!v["b"].is_f64());
90    /// assert!(!v["c"].is_f64());
91    /// ```
92    #[inline]
93    pub fn is_f64(&self) -> bool {
94        match self.n {
95            N::Float(_) => true,
96            N::PosInt(_) | N::NegInt(_) => false,
97        }
98    }
99
100    /// If the `Number` is an integer, represent it as i64 if possible. Returns
101    /// None otherwise.
102    ///
103    /// ```
104    /// # use lexpr::sexp;
105    /// #
106    /// let big = i64::max_value() as u64 + 10;
107    /// let v = sexp!(((a . 64) (b . ,big) (c . 256.0)));
108    ///
109    /// assert_eq!(v["a"].as_i64(), Some(64));
110    /// assert_eq!(v["b"].as_i64(), None);
111    /// assert_eq!(v["c"].as_i64(), None);
112    /// ```
113    #[inline]
114    pub fn as_i64(&self) -> Option<i64> {
115        match self.n {
116            N::PosInt(n) => {
117                if n <= i64::max_value() as u64 {
118                    Some(n as i64)
119                } else {
120                    None
121                }
122            }
123            N::NegInt(n) => Some(n),
124            N::Float(_) => None,
125        }
126    }
127
128    /// If the `Number` is an integer, represent it as u64 if possible. Returns
129    /// None otherwise.
130    ///
131    /// ```
132    /// # use lexpr::sexp;
133    /// #
134    /// let v = sexp!(((a . 64) (b . -64) (c . 256.0)));
135    ///
136    /// assert_eq!(v["a"].as_u64(), Some(64));
137    /// assert_eq!(v["b"].as_u64(), None);
138    /// assert_eq!(v["c"].as_u64(), None);
139    /// ```
140    #[inline]
141    pub fn as_u64(&self) -> Option<u64> {
142        match self.n {
143            N::PosInt(n) => Some(n),
144            N::NegInt(_) | N::Float(_) => None,
145        }
146    }
147
148    /// Represents the number as f64 if possible. Returns None otherwise.
149    ///
150    /// ```
151    /// # use lexpr::sexp;
152    /// #
153    /// let v = sexp!(((a . 256.0) (b . 64) (c . -64)));
154    ///
155    /// assert_eq!(v["a"].as_f64(), Some(256.0));
156    /// assert_eq!(v["b"].as_f64(), Some(64.0));
157    /// assert_eq!(v["c"].as_f64(), Some(-64.0));
158    /// ```
159    #[inline]
160    pub fn as_f64(&self) -> Option<f64> {
161        match self.n {
162            N::PosInt(n) => Some(n as f64),
163            N::NegInt(n) => Some(n as f64),
164            N::Float(n) => Some(n),
165        }
166    }
167
168    /// Converts a finite `f64` to a `Number`. Infinite or NaN values
169    /// are not S-expression numbers.
170    ///
171    /// ```
172    /// # use std::f64;
173    /// #
174    /// # use lexpr::Number;
175    /// #
176    /// assert!(Number::from_f64(256.0).is_some());
177    ///
178    /// assert!(Number::from_f64(f64::NAN).is_none());
179    /// ```
180    #[inline]
181    pub fn from_f64(f: f64) -> Option<Number> {
182        if f.is_finite() {
183            Some(Number { n: N::Float(f) })
184        } else {
185            None
186        }
187    }
188
189    /// Dispatch based on the type of the contained value.
190    ///
191    /// Depending on the stored value, one of the functions of the
192    /// supplied visitor will be called.
193    pub fn visit<V>(&self, visitor: V) -> Result<V::Value, V::Error>
194    where
195        V: Visitor,
196    {
197        match self.n {
198            N::PosInt(n) => visitor.visit_u64(n),
199            N::NegInt(n) => visitor.visit_i64(n),
200            N::Float(n) => visitor.visit_f64(n),
201        }
202    }
203}
204
205/// Trait to access the value stored in `Number`.
206///
207/// The `Number` type does not directly expose its internal
208/// structure to allow future changes without breaking the API.
209///
210/// Instead, you can implement this trait and pass your implementation
211/// to `Number::visit`.
212///
213/// [`Number::visit`]: struct.Number.html#method.visit
214pub trait Visitor {
215    /// The return type of the visitor methods.
216    type Value;
217    /// The error type of the visitor methods.
218    type Error;
219
220    /// Construct an error given a message.
221    ///
222    /// This method is used by trait default implementations.
223    fn error<T: Into<String>>(msg: T) -> Self::Error;
224
225    /// The stored value is a `u64`.
226    fn visit_u64(self, n: u64) -> Result<Self::Value, Self::Error>;
227    /// The stored value is an `i64`.
228    fn visit_i64(self, n: i64) -> Result<Self::Value, Self::Error>;
229    /// The stored value is `f64`.
230    fn visit_f64(self, n: f64) -> Result<Self::Value, Self::Error>;
231}
232
233macro_rules! impl_from_unsigned {
234    (
235        $($ty:ty),*
236    ) => {
237        $(
238            impl From<$ty> for Number {
239                #[inline]
240                fn from(u: $ty) -> Self {
241                    Number { n: N::PosInt(u64::from(u)) }
242                }
243            }
244        )*
245    };
246}
247
248macro_rules! impl_from_signed {
249    (
250        $($ty:ty),*
251    ) => {
252        $(
253            impl From<$ty> for Number {
254                #[inline]
255                fn from(n: $ty) -> Self {
256                    let n = if n >= 0 {
257                        N::PosInt(n as u64)
258                    } else {
259                        N::NegInt(i64::from(n))
260                    };
261                    Number { n }
262                }
263            }
264        )*
265    };
266}
267
268impl_from_unsigned!(u8, u16, u32, u64);
269impl_from_signed!(i8, i16, i32, i64);
270
271impl From<f32> for Number {
272    #[inline]
273    fn from(n: f32) -> Self {
274        Number {
275            n: N::Float(f64::from(n)),
276        }
277    }
278}
279
280impl From<f64> for Number {
281    #[inline]
282    fn from(n: f64) -> Self {
283        Number { n: N::Float(n) }
284    }
285}
286
287impl Display for Number {
288    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
289        match self.n {
290            N::PosInt(i) => Display::fmt(&i, formatter),
291            N::NegInt(i) => Display::fmt(&i, formatter),
292            N::Float(f) => Display::fmt(&f, formatter),
293        }
294    }
295}
296
297impl Debug for Number {
298    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
299        Debug::fmt(&self.n, formatter)
300    }
301}