pub mod error;
use crate::num::error::{JsonIntOverflowError, JsonIntParseError};
use std::{
fmt::{self, Display, Formatter},
num::{NonZeroU32, NonZeroU64},
str::FromStr,
};
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct JsonInt(i64);
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct JsonUInt(u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct JsonNonZeroUInt(NonZeroU64);
const JSON_UINT_UPPER_LIMIT: u64 = (1 << 53) - 1;
const JSON_INT_UPPER_LIMIT: i64 = (1 << 53) - 1;
const JSON_INT_LOWER_LIMIT: i64 = -(1 << 53) + 1;
impl JsonInt {
pub const ZERO: Self = Self::new(0);
pub const ONE: Self = Self::new(1);
pub const MIN: Self = Self::new(JSON_INT_LOWER_LIMIT);
pub const MAX: Self = Self::new(JSON_INT_UPPER_LIMIT);
#[must_use]
const fn new(index: i64) -> Self {
Self(index)
}
#[inline]
pub fn try_increment(&mut self) -> Result<(), JsonIntOverflowError> {
let new_index = self.0 + 1;
if new_index <= JSON_INT_UPPER_LIMIT {
self.0 = new_index;
Ok(())
} else {
Err(JsonIntOverflowError::int_neg_overflow(new_index))
}
}
#[must_use]
#[inline(always)]
pub const fn as_i64(&self) -> i64 {
self.0
}
#[must_use]
#[inline(always)]
pub const fn neg(&self) -> Self {
Self(-self.0)
}
#[inline(always)]
#[must_use]
pub const fn abs(&self) -> JsonUInt {
JsonUInt(self.0.unsigned_abs())
}
}
impl JsonUInt {
pub const ZERO: Self = Self::new(0);
pub const ONE: Self = Self::new(1);
pub const MAX: Self = Self::new(JSON_UINT_UPPER_LIMIT);
#[must_use]
const fn new(index: u64) -> Self {
Self(index)
}
#[inline]
pub fn try_increment(&mut self) -> Result<(), JsonIntOverflowError> {
let new_index = self.0 + 1;
if new_index <= JSON_UINT_UPPER_LIMIT {
self.0 = new_index;
Ok(())
} else {
Err(JsonIntOverflowError::uint_pos_overflow(new_index))
}
}
#[must_use]
#[inline(always)]
pub const fn neg(&self) -> JsonInt {
JsonInt(-(self.0 as i64))
}
#[must_use]
#[inline(always)]
pub const fn as_u64(&self) -> u64 {
self.0
}
}
impl JsonNonZeroUInt {
#[must_use]
const fn new(value: NonZeroU64) -> Self {
Self(value)
}
#[must_use]
#[inline(always)]
pub const fn as_non_zero_u64(&self) -> NonZeroU64 {
self.0
}
#[must_use]
#[inline(always)]
pub const fn as_u64(&self) -> u64 {
self.0.get()
}
}
impl TryFrom<i64> for JsonInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: i64) -> Result<Self, Self::Error> {
if value > JSON_INT_UPPER_LIMIT {
Err(JsonIntOverflowError::int_pos_overflow(value))
} else if value < JSON_INT_LOWER_LIMIT {
Err(JsonIntOverflowError::int_neg_overflow(value))
} else {
Ok(Self::new(value))
}
}
}
impl TryFrom<u64> for JsonInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: u64) -> Result<Self, Self::Error> {
if value > i64::MAX as u64 {
Err(JsonIntOverflowError::int_pos_overflow_u(value))
} else {
Self::try_from(value as i64)
}
}
}
impl From<i32> for JsonInt {
#[inline]
fn from(value: i32) -> Self {
Self::new(i64::from(value))
}
}
impl From<u32> for JsonInt {
#[inline]
fn from(value: u32) -> Self {
Self::new(i64::from(value))
}
}
impl From<JsonInt> for i64 {
#[inline(always)]
fn from(value: JsonInt) -> Self {
value.0
}
}
impl From<JsonUInt> for JsonInt {
#[inline(always)]
fn from(value: JsonUInt) -> Self {
Self::new(value.0 as i64)
}
}
impl FromStr for JsonInt {
type Err = JsonIntParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
match i64::from_str(s) {
Ok(x) => x.try_into().map_err(|e| Self::Err::parse_conversion_err(s, &e)),
Err(err) => Err(Self::Err::int_parse_error(s, err.kind())),
}
}
}
impl TryFrom<u64> for JsonUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: u64) -> Result<Self, Self::Error> {
if value > JSON_UINT_UPPER_LIMIT {
Err(JsonIntOverflowError::uint_pos_overflow(value))
} else {
Ok(Self::new(value))
}
}
}
impl TryFrom<i64> for JsonUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: i64) -> Result<Self, Self::Error> {
if value < 0 {
Err(JsonIntOverflowError::negative_uint(value))
} else {
Self::try_from(value as u64)
}
}
}
impl From<u32> for JsonUInt {
#[inline]
fn from(value: u32) -> Self {
Self::new(u64::from(value))
}
}
impl TryFrom<i32> for JsonUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value < 0 {
Err(JsonIntOverflowError::negative_uint(i64::from(value)))
} else {
Ok(Self::from(value as u32))
}
}
}
impl From<JsonUInt> for u64 {
#[inline(always)]
fn from(value: JsonUInt) -> Self {
value.0
}
}
impl TryFrom<JsonInt> for JsonUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: JsonInt) -> Result<Self, Self::Error> {
if value.0 < 0 {
Err(JsonIntOverflowError::negative_uint(value.0))
} else {
Ok(Self::new(value.0 as u64))
}
}
}
impl FromStr for JsonUInt {
type Err = JsonIntParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
match i64::from_str(s) {
Ok(x) => x.try_into().map_err(|e| Self::Err::parse_conversion_err(s, &e)),
Err(err) => Err(Self::Err::uint_parse_error(s, err.kind())),
}
}
}
impl From<NonZeroU32> for JsonNonZeroUInt {
#[inline]
fn from(value: NonZeroU32) -> Self {
Self::new(NonZeroU64::from(value))
}
}
impl From<NonZeroU64> for JsonNonZeroUInt {
#[inline]
fn from(value: NonZeroU64) -> Self {
Self::new(value)
}
}
impl TryFrom<u32> for JsonNonZeroUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::try_from(u64::from(value))
}
}
impl TryFrom<i32> for JsonNonZeroUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: i32) -> Result<Self, Self::Error> {
Self::try_from(i64::from(value))
}
}
impl TryFrom<u64> for JsonNonZeroUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: u64) -> Result<Self, Self::Error> {
if value > JSON_UINT_UPPER_LIMIT {
Err(JsonIntOverflowError::uint_pos_overflow(value))
} else if let Some(x) = NonZeroU64::new(value) {
Ok(Self(x))
} else {
Err(JsonIntOverflowError::zero_non_zero_uint())
}
}
}
impl TryFrom<i64> for JsonNonZeroUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: i64) -> Result<Self, Self::Error> {
if value < 0 {
Err(JsonIntOverflowError::negative_uint(value))
} else {
Self::try_from(value as u64)
}
}
}
impl TryFrom<JsonUInt> for JsonNonZeroUInt {
type Error = JsonIntOverflowError;
#[inline]
fn try_from(value: JsonUInt) -> Result<Self, Self::Error> {
Self::try_from(value.0)
}
}
impl From<JsonNonZeroUInt> for JsonUInt {
#[inline]
fn from(value: JsonNonZeroUInt) -> Self {
Self::new(value.0.get())
}
}
impl FromStr for JsonNonZeroUInt {
type Err = JsonIntParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
match i64::from_str(s) {
Ok(x) => x.try_into().map_err(|e| Self::Err::parse_conversion_err(s, &e)),
Err(err) => Err(Self::Err::non_zero_uint_parse_error(s, err.kind())),
}
}
}
impl Display for JsonInt {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Display for JsonUInt {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Display for JsonNonZeroUInt {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(feature = "arbitrary")]
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
impl<'a> arbitrary::Arbitrary<'a> for JsonInt {
#[inline]
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let val = u.int_in_range(JSON_INT_LOWER_LIMIT..=JSON_INT_UPPER_LIMIT)?;
Ok(Self::new(val))
}
}
#[cfg(feature = "arbitrary")]
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
impl<'a> arbitrary::Arbitrary<'a> for JsonUInt {
#[inline]
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let val = u.int_in_range(0..=JSON_UINT_UPPER_LIMIT)?;
Ok(Self::new(val))
}
}
#[cfg(feature = "arbitrary")]
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
impl<'a> arbitrary::Arbitrary<'a> for JsonNonZeroUInt {
#[inline]
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let val = u.int_in_range(1..=JSON_UINT_UPPER_LIMIT)?;
Ok(Self::new(NonZeroU64::new(val).expect("range starts at 1")))
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn int_upper_limit_sanity_check() {
assert_eq!(JSON_INT_UPPER_LIMIT, (1 << 53) - 1);
assert_eq!(JSON_INT_UPPER_LIMIT, 9_007_199_254_740_991);
}
#[test]
fn int_lower_limit_sanity_check() {
assert_eq!(JSON_INT_LOWER_LIMIT, -(1 << 53) + 1);
assert_eq!(JSON_INT_LOWER_LIMIT, -9_007_199_254_740_991);
assert_eq!(JSON_INT_LOWER_LIMIT, -JSON_INT_UPPER_LIMIT);
}
#[test]
fn uint_upper_limit_sanity_check() {
assert_eq!(JSON_UINT_UPPER_LIMIT, (1 << 53) - 1);
assert_eq!(JSON_UINT_UPPER_LIMIT, 9_007_199_254_740_991);
assert_eq!(JSON_INT_UPPER_LIMIT, JSON_UINT_UPPER_LIMIT as i64);
}
#[test]
fn int_lower_limit_try_from_check() {
let min = JsonInt::try_from(JSON_INT_LOWER_LIMIT).expect("JSON int lower_limit should be convertible.");
let err = JsonInt::try_from(JSON_INT_LOWER_LIMIT - 1)
.expect_err("Values below JSON int lower_limit should not be convertible.");
assert_eq!(min.as_i64(), JSON_INT_LOWER_LIMIT);
assert_eq!(
err.to_string(),
"value -9007199254740992 is below the range of JsonInt values [-9007199254740991..9007199254740991]"
);
}
#[test]
fn int_upper_limit_try_from_check() {
let max = JsonInt::try_from(JSON_INT_UPPER_LIMIT).expect("JSON int upper_limit should be convertible.");
let err = JsonInt::try_from(JSON_INT_UPPER_LIMIT + 1)
.expect_err("Values in excess of JSON int upper_limit should not be convertible.");
assert_eq!(max.as_i64(), JSON_INT_UPPER_LIMIT);
assert_eq!(
err.to_string(),
"value 9007199254740992 is above the range of JsonInt values [-9007199254740991..9007199254740991]"
);
}
#[test]
fn uint_upper_limit_try_from_check() {
let max = JsonUInt::try_from(JSON_UINT_UPPER_LIMIT).expect("JSON uint upper_limit should be convertible.");
let err = JsonUInt::try_from(JSON_UINT_UPPER_LIMIT + 1)
.expect_err("Values in excess of JSON uint upper_limit should not be convertible.");
assert_eq!(max.as_u64(), JSON_UINT_UPPER_LIMIT);
assert_eq!(
err.to_string(),
"value 9007199254740992 is above the range of JsonUInt values [0..9007199254740991]"
);
}
#[test]
fn non_zero_uint_try_from_zero_check() {
let err_i32 = JsonNonZeroUInt::try_from(0_i32).expect_err("zero should not be convertible");
let err_u32 = JsonNonZeroUInt::try_from(0_u32).expect_err("zero should not be convertible");
let err_i64 = JsonNonZeroUInt::try_from(0_i64).expect_err("zero should not be convertible");
let err_u64 = JsonNonZeroUInt::try_from(0_u64).expect_err("zero should not be convertible");
assert_eq!(
err_i32.to_string(),
"attempt to convert a zero value into a JsonNonZeroUInt"
);
assert_eq!(
err_u32.to_string(),
"attempt to convert a zero value into a JsonNonZeroUInt"
);
assert_eq!(
err_i64.to_string(),
"attempt to convert a zero value into a JsonNonZeroUInt"
);
assert_eq!(
err_u64.to_string(),
"attempt to convert a zero value into a JsonNonZeroUInt"
);
}
#[test]
fn parse_int_from_empty() {
let err = JsonInt::from_str("").expect_err("empty string is not valid");
assert_eq!(
err.to_string(),
"string '' is not a valid representation of a JSON integer"
);
}
#[test]
fn parse_int_underflow() {
let err = JsonInt::from_str("-9007199254740992").expect_err("out of range");
assert_eq!(
err.to_string(),
"string '-9007199254740992' represents a value below the range of JsonInt values [-9007199254740991..9007199254740991]"
);
}
#[test]
fn parse_int_overflow() {
let err = JsonInt::from_str("9007199254740992").expect_err("out of range");
assert_eq!(
err.to_string(),
"string '9007199254740992' represents a value above the range of JsonInt values [-9007199254740991..9007199254740991]"
);
}
#[test]
fn parse_int_from_invalid_characters() {
let err = JsonInt::from_str("42+7").expect_err("not a valid integer");
assert_eq!(
err.to_string(),
"string '42+7' is not a valid representation of a JSON integer"
);
}
#[test]
fn parse_uint_from_empty() {
let err = JsonUInt::from_str("").expect_err("empty string is not valid");
assert_eq!(
err.to_string(),
"string '' is not a valid representation of a JSON integer"
);
}
#[test]
fn parse_uint_from_negative() {
let err = JsonUInt::from_str("-42").expect_err("out of range");
assert_eq!(
err.to_string(),
"string '-42' represents a value below the range of JsonUInt values [0..9007199254740991]"
);
}
#[test]
fn parse_uint_overflow() {
let err = JsonUInt::from_str("9007199254740992").expect_err("out of range");
assert_eq!(
err.to_string(),
"string '9007199254740992' represents a value above the range of JsonUInt values [0..9007199254740991]"
);
}
#[test]
fn parse_uint_from_invalid_characters() {
let err = JsonUInt::from_str("42+7").expect_err("not a valid integer");
assert_eq!(
err.to_string(),
"string '42+7' is not a valid representation of a JSON integer"
);
}
#[test]
fn parse_non_zero_uint_from_zero() {
let err = JsonNonZeroUInt::from_str("0").expect_err("not a non-zero integer");
assert_eq!(
err.to_string(),
"string '0' represents a zero value, which is not a valid JsonNonZeroUInt"
)
}
mod proptests {
use super::super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn int_roundtrip(value in JSON_INT_LOWER_LIMIT..JSON_INT_UPPER_LIMIT) {
let json_int = JsonInt::try_from(value).expect("within range");
assert_eq!(json_int.as_i64(), value);
}
#[test]
fn uint_roundtrip(value in 0..JSON_UINT_UPPER_LIMIT) {
let json_uint = JsonUInt::try_from(value).expect("within range");
assert_eq!(json_uint.as_u64(), value);
}
#[test]
fn int_string_roundtrip(value in JSON_INT_LOWER_LIMIT..JSON_INT_UPPER_LIMIT) {
let string = value.to_string();
let json_int = JsonInt::from_str(&string).expect("valid string");
assert_eq!(string, json_int.to_string())
}
#[test]
fn uint_string_roundtrip(value in 0..JSON_UINT_UPPER_LIMIT) {
let string = value.to_string();
let json_int = JsonUInt::from_str(&string).expect("valid string");
assert_eq!(string, json_int.to_string())
}
#[test]
fn int_increment(value in JSON_INT_LOWER_LIMIT..(JSON_INT_UPPER_LIMIT - 1)) {
let mut json_int = JsonInt::try_from(value).expect("within range");
json_int.try_increment().expect("at most one below limit");
assert_eq!(json_int.as_i64(), value + 1);
}
#[test]
fn uint_increment(value in 0..(JSON_UINT_UPPER_LIMIT - 1)) {
let mut json_uint = JsonUInt::try_from(value).expect("within range");
json_uint.try_increment().expect("at most one below limit");
assert_eq!(json_uint.as_u64(), value + 1);
}
}
}
}