1use alloc::boxed::Box;
4use alloc::string::{String, ToString};
5
6use crate::error::JsonNumberError;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct JsonNumber {
16 repr: Box<str>,
17}
18
19impl JsonNumber {
20 pub fn new(input: &str) -> Result<Self, JsonNumberError> {
23 if is_valid_json_number(input) {
24 Ok(Self { repr: input.into() })
25 } else {
26 Err(JsonNumberError::InvalidNumber)
27 }
28 }
29
30 pub(crate) fn from_validated(repr: String) -> Self {
32 Self {
33 repr: repr.into_boxed_str(),
34 }
35 }
36
37 pub fn as_str(&self) -> &str {
39 &self.repr
40 }
41
42 pub fn is_integer(&self) -> bool {
44 !self
45 .repr
46 .bytes()
47 .any(|b| b == b'.' || b == b'e' || b == b'E')
48 }
49
50 pub fn to_i64(&self) -> Result<i64, JsonNumberError> {
53 if !self.is_integer() {
54 return Err(JsonNumberError::NotAnInteger);
55 }
56 self.repr
57 .parse::<i64>()
58 .map_err(|_| JsonNumberError::OutOfRange)
59 }
60
61 pub fn to_u64(&self) -> Result<u64, JsonNumberError> {
64 if !self.is_integer() {
65 return Err(JsonNumberError::NotAnInteger);
66 }
67 self.repr
68 .parse::<u64>()
69 .map_err(|_| JsonNumberError::OutOfRange)
70 }
71
72 pub fn to_f64(&self) -> Result<f64, JsonNumberError> {
75 match self.repr.parse::<f64>() {
76 Ok(value) if value.is_finite() => Ok(value),
77 _ => Err(JsonNumberError::NotFinite),
78 }
79 }
80
81 pub fn try_from_f64(value: f64) -> Result<Self, JsonNumberError> {
83 if !value.is_finite() {
84 return Err(JsonNumberError::NotFinite);
85 }
86 let repr = value.to_string();
87 if is_valid_json_number(&repr) {
90 Ok(Self {
91 repr: repr.into_boxed_str(),
92 })
93 } else {
94 Err(JsonNumberError::InvalidNumber)
95 }
96 }
97}
98
99pub(crate) fn is_valid_json_number(s: &str) -> bool {
102 let b = s.as_bytes();
103 let n = b.len();
104 let mut i = 0;
105
106 if i < n && b[i] == b'-' {
107 i += 1;
108 }
109
110 match b.get(i) {
112 Some(b'0') => i += 1,
113 Some(d) if d.is_ascii_digit() => {
114 i += 1;
115 while i < n && b[i].is_ascii_digit() {
116 i += 1;
117 }
118 }
119 _ => return false,
120 }
121
122 if i < n && b[i] == b'.' {
124 i += 1;
125 let start = i;
126 while i < n && b[i].is_ascii_digit() {
127 i += 1;
128 }
129 if i == start {
130 return false;
131 }
132 }
133
134 if i < n && (b[i] == b'e' || b[i] == b'E') {
136 i += 1;
137 if i < n && (b[i] == b'+' || b[i] == b'-') {
138 i += 1;
139 }
140 let start = i;
141 while i < n && b[i].is_ascii_digit() {
142 i += 1;
143 }
144 if i == start {
145 return false;
146 }
147 }
148
149 i == n
150}