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}