1use alloc::borrow::Cow;
2use alloc::format;
3use alloc::string::ToString;
4use core::fmt;
5
6use crate::State;
7use crate::de::{Deserialize, Slot, default_atom};
8use crate::error::Error;
9use crate::event::Atom;
10use crate::ext::known::invalid;
11use crate::ext::{BorrowedExtension, ExtValue};
12use crate::ser::{Emit, 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_owned(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_owned()
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<'b>(value: &'b Self, _state: &mut State) -> Result<Emit<'b>, Error> {
166 Ok(Emit::Atom(Atom::Ext(ExtValue::borrowed_value::<Number>(
167 value,
168 ))))
169 }
170}
171
172impl<'de, 'a> Deserialize<'de> for Number<'a> {
175 fn deserialize_atom(slot: &mut Slot<Self>, atom: Atom, state: &mut State) -> Result<(), Error> {
176 let number = match atom {
177 Atom::Ext(ref ext) => match ext.downcast_value_ref::<Number>() {
178 Some(value) => value.clone().into_owned(),
179 None => return default_atom(slot, atom, state),
180 },
181 Atom::U64(value) => Number::new(value.to_string(), value as f64),
182 Atom::I64(value) => Number::new(value.to_string(), value as f64),
183 Atom::F64(value) if value.is_finite() => Number::new(format!("{:?}", value), value),
184 Atom::F32(value) if value.is_finite() => Number::parse(format!("{:?}", value))?,
186 Atom::Str(ref value) => Number::parse(value.to_string())?,
187 other => return default_atom(slot, other, state),
188 };
189 slot.set(number);
190 Ok(())
191 }
192
193 fn expecting() -> Cow<'static, str> {
194 Cow::Borrowed("number")
195 }
196}
197
198#[test]
199fn test_number() {
200 let number = Number::parse("-12.50e3").unwrap();
201 assert_eq!(number.as_str(), "-12.50e3");
202 assert_eq!(number.value(), -12500.0);
203 assert!(!number.is_integer());
204 assert!(
205 Number::parse("12345678901234567890123456789")
206 .unwrap()
207 .is_integer()
208 );
209 for invalid in ["", "1.", ".1", "+1", "01", "1e", "NaN", "inf", "0x10"] {
210 assert!(Number::parse(invalid).is_err(), "{}", invalid);
211 }
212
213 let ext = ExtValue::borrowed_value::<Number>(&number);
214 assert_eq!(ext.fallback(), Atom::F64(-12500.0));
215 assert_eq!(ext.name(), "number");
216 assert_eq!(
217 ext.downcast_value_ref::<Number>().unwrap().as_str(),
218 "-12.50e3"
219 );
220}