1use alloc::borrow::Cow;
2use alloc::format;
3use alloc::string::ToString;
4use core::fmt;
5
6use crate::State;
7use crate::de::{Deserialize, Sink, SinkHandle};
8use crate::error::Error;
9use crate::event::Atom;
10use crate::ext::known::invalid;
11use crate::ext::{BorrowedExtension, ExtValue};
12use crate::ser::{Chunk, Serialize};
13
14#[derive(Clone, PartialEq)]
39pub struct Number<'a> {
40 text: Cow<'a, str>,
41 value: f64,
42}
43
44impl<'a> Number<'a> {
45 pub fn new<T: Into<Cow<'a, str>>>(text: T, value: f64) -> Number<'a> {
52 let text = text.into();
53 debug_assert!(is_json_number(&text), "invalid number text {:?}", text);
54 Number { text, value }
55 }
56
57 pub fn parse<T: Into<Cow<'a, str>>>(text: T) -> Result<Number<'a>, Error> {
61 let text = text.into();
62 if !is_json_number(&text) {
63 return Err(invalid("invalid number"));
64 }
65 let value = text.parse().map_err(|_| invalid("invalid number"))?;
66 Ok(Number { text, value })
67 }
68
69 pub fn as_str(&self) -> &str {
71 &self.text
72 }
73
74 pub fn value(&self) -> f64 {
78 self.value
79 }
80
81 pub fn is_integer(&self) -> bool {
83 !self.text.contains(['.', 'e', 'E'])
84 }
85
86 pub fn into_static(self) -> Number<'static> {
88 Number {
89 text: Cow::Owned(self.text.into_owned()),
90 value: self.value,
91 }
92 }
93}
94
95pub(crate) fn is_json_number(s: &str) -> bool {
97 let bytes = s.as_bytes();
98 let mut pos = 0;
99 let digits = |pos: &mut usize| {
100 let start = *pos;
101 while bytes.get(*pos).is_some_and(u8::is_ascii_digit) {
102 *pos += 1;
103 }
104 *pos - start
105 };
106 if bytes.first() == Some(&b'-') {
107 pos += 1;
108 }
109 let int_start = pos;
110 let int_len = digits(&mut pos);
111 if int_len == 0 || (int_len > 1 && bytes[int_start] == b'0') {
112 return false;
113 }
114 if bytes.get(pos) == Some(&b'.') {
115 pos += 1;
116 if digits(&mut pos) == 0 {
117 return false;
118 }
119 }
120 if let Some(b'e' | b'E') = bytes.get(pos) {
121 pos += 1;
122 if let Some(b'+' | b'-') = bytes.get(pos) {
123 pos += 1;
124 }
125 if digits(&mut pos) == 0 {
126 return false;
127 }
128 }
129 pos == bytes.len()
130}
131
132impl<'a> fmt::Display for Number<'a> {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 f.write_str(&self.text)
135 }
136}
137
138impl<'a> fmt::Debug for Number<'a> {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 write!(f, "Number({})", self.text)
141 }
142}
143
144impl BorrowedExtension for Number<'static> {
145 type Value<'a> = Number<'a>;
146
147 fn name<'v>(_value: &'v Number<'_>) -> &'v str {
148 "number"
149 }
150
151 fn fallback<'v>(value: &'v Number<'_>) -> Atom<'v> {
152 Atom::F64(value.value)
153 }
154
155 fn to_static(value: &Number<'_>) -> Number<'static> {
156 value.clone().into_static()
157 }
158
159 fn shorten<'s, 'l: 's>(value: &'s Number<'l>) -> &'s Number<'s> {
160 value
161 }
162}
163
164impl<'a> Serialize for Number<'a> {
165 fn serialize(&self, _state: &mut State) -> Result<Chunk<'_>, Error> {
166 Ok(Chunk::Atom(Atom::Ext(ExtValue::borrowed_value::<Number>(
167 self,
168 ))))
169 }
170}
171
172impl<'de, 'a> Deserialize<'de> for Number<'a> {
175 fn deserialize_into<'out>(
176 out: &'out mut Option<Self>,
177 state: &mut State,
178 ) -> SinkHandle<'out, 'de> {
179 SinkHandle::arena(NumberSink(out), state)
180 }
181}
182
183struct NumberSink<'a, 'n>(&'a mut Option<Number<'n>>);
184
185impl<'a, 'n, 'de> Sink<'de> for NumberSink<'a, 'n> {
186 fn expecting(&self) -> Cow<'_, str> {
187 "number".into()
188 }
189
190 fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
191 let number = match atom {
192 Atom::Ext(ref ext) => match ext.downcast_value_ref::<Number>() {
193 Some(value) => value.clone().into_static(),
194 None => return self.unexpected_atom(atom, state),
195 },
196 Atom::U64(value) => Number::new(value.to_string(), value as f64),
197 Atom::I64(value) => Number::new(value.to_string(), value as f64),
198 Atom::F64(value) if value.is_finite() => Number::new(format!("{:?}", value), value),
199 Atom::F32(value) if value.is_finite() => Number::parse(format!("{:?}", value))?,
201 Atom::Str(ref value) => Number::parse(value.to_string())?,
202 other => return self.unexpected_atom(other, state),
203 };
204 *self.0 = Some(number);
205 Ok(())
206 }
207}
208
209#[test]
210fn test_number() {
211 let number = Number::parse("-12.50e3").unwrap();
212 assert_eq!(number.as_str(), "-12.50e3");
213 assert_eq!(number.value(), -12500.0);
214 assert!(!number.is_integer());
215 assert!(
216 Number::parse("12345678901234567890123456789")
217 .unwrap()
218 .is_integer()
219 );
220 for invalid in ["", "1.", ".1", "+1", "01", "1e", "NaN", "inf", "0x10"] {
221 assert!(Number::parse(invalid).is_err(), "{}", invalid);
222 }
223
224 let ext = ExtValue::borrowed_value::<Number>(&number);
225 assert_eq!(ext.fallback(), Atom::F64(-12500.0));
226 assert_eq!(ext.name(), "number");
227 assert_eq!(
228 ext.downcast_value_ref::<Number>().unwrap().as_str(),
229 "-12.50e3"
230 );
231}