use alloc::boxed::Box;
use alloc::string::{String, ToString};
use crate::error::JsonNumberError;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct JsonNumber {
repr: Box<str>,
}
impl JsonNumber {
pub fn new(input: &str) -> Result<Self, JsonNumberError> {
if is_valid_json_number(input) {
Ok(Self { repr: input.into() })
} else {
Err(JsonNumberError::InvalidNumber)
}
}
pub(crate) fn from_validated(repr: String) -> Self {
Self {
repr: repr.into_boxed_str(),
}
}
pub fn as_str(&self) -> &str {
&self.repr
}
pub fn is_integer(&self) -> bool {
!self
.repr
.bytes()
.any(|b| b == b'.' || b == b'e' || b == b'E')
}
pub fn to_i64(&self) -> Result<i64, JsonNumberError> {
if !self.is_integer() {
return Err(JsonNumberError::NotAnInteger);
}
self.repr
.parse::<i64>()
.map_err(|_| JsonNumberError::OutOfRange)
}
pub fn to_u64(&self) -> Result<u64, JsonNumberError> {
if !self.is_integer() {
return Err(JsonNumberError::NotAnInteger);
}
self.repr
.parse::<u64>()
.map_err(|_| JsonNumberError::OutOfRange)
}
pub fn to_f64(&self) -> Result<f64, JsonNumberError> {
match self.repr.parse::<f64>() {
Ok(value) if value.is_finite() => Ok(value),
_ => Err(JsonNumberError::NotFinite),
}
}
pub fn try_from_f64(value: f64) -> Result<Self, JsonNumberError> {
if !value.is_finite() {
return Err(JsonNumberError::NotFinite);
}
let repr = value.to_string();
if is_valid_json_number(&repr) {
Ok(Self {
repr: repr.into_boxed_str(),
})
} else {
Err(JsonNumberError::InvalidNumber)
}
}
}
pub(crate) fn is_valid_json_number(s: &str) -> bool {
let b = s.as_bytes();
let n = b.len();
let mut i = 0;
if i < n && b[i] == b'-' {
i += 1;
}
match b.get(i) {
Some(b'0') => i += 1,
Some(d) if d.is_ascii_digit() => {
i += 1;
while i < n && b[i].is_ascii_digit() {
i += 1;
}
}
_ => return false,
}
if i < n && b[i] == b'.' {
i += 1;
let start = i;
while i < n && b[i].is_ascii_digit() {
i += 1;
}
if i == start {
return false;
}
}
if i < n && (b[i] == b'e' || b[i] == b'E') {
i += 1;
if i < n && (b[i] == b'+' || b[i] == b'-') {
i += 1;
}
let start = i;
while i < n && b[i].is_ascii_digit() {
i += 1;
}
if i == start {
return false;
}
}
i == n
}