use std::{fmt::Debug, io::Write};
use duplicate::duplicate_item;
use thiserror::Error;
use crate::json_number::is_valid_json_number;
mod stream_writer;
pub use stream_writer::*;
#[cfg(feature = "simple-api")]
pub mod simple;
type IoError = std::io::Error;
pub trait JsonWriter {
type WriterResult;
fn begin_object(&mut self) -> Result<(), IoError>;
fn end_object(&mut self) -> Result<(), IoError>;
fn begin_array(&mut self) -> Result<(), IoError>;
fn end_array(&mut self) -> Result<(), IoError>;
fn name(&mut self, name: &str) -> Result<(), IoError>;
fn null_value(&mut self) -> Result<(), IoError>;
fn bool_value(&mut self, value: bool) -> Result<(), IoError>;
fn string_value(&mut self, value: &str) -> Result<(), IoError>;
fn string_value_writer(&mut self) -> Result<impl StringValueWriter + '_, IoError>;
fn number_value_from_string(&mut self, value: &str) -> Result<(), JsonNumberError>;
fn number_value<N: FiniteNumber>(&mut self, value: N) -> Result<(), IoError>;
fn fp_number_value<N: FloatingPointNumber>(&mut self, value: N) -> Result<(), JsonNumberError>;
#[cfg(feature = "serde")]
fn serialize_value<S: serde_core::ser::Serialize>(
&mut self,
value: &S,
) -> Result<(), crate::serde::SerializerError>;
fn finish_document(self) -> Result<Self::WriterResult, IoError>;
}
pub trait StringValueWriter: Write {
fn write_str(&mut self, s: &str) -> Result<(), IoError> {
self.write_all(s.as_bytes())
}
fn finish_value(self) -> Result<(), IoError>;
}
pub trait FiniteNumber: private::Sealed {
fn use_json_number<C: FnOnce(&str) -> Result<(), IoError>>(
&self,
consumer: C,
) -> Result<(), IoError>;
fn as_u64(&self) -> Option<u64>;
fn as_i64(&self) -> Option<i64>;
}
pub trait FloatingPointNumber: private::Sealed {
fn use_json_number<C: FnOnce(&str) -> Result<(), IoError>>(
&self,
consumer: C,
) -> Result<(), JsonNumberError>;
fn as_f64(&self) -> Option<f64>;
}
mod private {
use super::*;
pub trait Sealed {}
#[duplicate_item(type_template; [u8]; [i8]; [u16]; [i16]; [u32]; [i32]; [u64]; [i64]; [u128]; [i128]; [usize]; [isize]; [f32]; [f64])]
impl Sealed for type_template {}
}
#[derive(Error, Debug)]
pub enum JsonNumberError {
#[error("{0}")]
InvalidNumber(String),
#[error("IO error: {0}")]
IoError(#[from] IoError),
}
#[duplicate_item(type_template; [u8]; [i8]; [u16]; [i16]; [u32]; [i32]; [u64]; [i64]; [u128]; [i128]; [usize]; [isize])]
impl FiniteNumber for type_template {
#[inline(always)]
fn use_json_number<C: FnOnce(&str) -> Result<(), IoError>>(
&self,
consumer: C,
) -> Result<(), IoError> {
let string = self.to_string();
debug_assert!(
is_valid_json_number(&string),
"Unexpected: Not a valid JSON number: {string}"
);
consumer(&string)
}
fn as_u64(&self) -> Option<u64> {
#[allow(
clippy::useless_conversion,
clippy::unnecessary_fallible_conversions,
reason = "for u64 -> u64"
)]
(*self).try_into().ok()
}
fn as_i64(&self) -> Option<i64> {
#[allow(
clippy::useless_conversion,
clippy::unnecessary_fallible_conversions,
reason = "for i64 -> i64"
)]
(*self).try_into().ok()
}
}
#[duplicate_item(type_template; [f32]; [f64])]
impl FloatingPointNumber for type_template {
#[inline(always)]
fn use_json_number<C: FnOnce(&str) -> Result<(), IoError>>(
&self,
consumer: C,
) -> Result<(), JsonNumberError> {
if self.is_finite() {
let string = self.to_string();
debug_assert!(
is_valid_json_number(&string),
"Unexpected: Not a valid JSON number: {string}"
);
consumer(&string)?;
Ok(())
} else {
Err(JsonNumberError::InvalidNumber(format!(
"non-finite number: {self}"
)))
}
}
fn as_f64(&self) -> Option<f64> {
#[allow(clippy::useless_conversion, reason = "for f64 -> f64")]
Some((*self).into())
}
}
#[derive(Debug)]
pub(crate) struct TransferredNumber<'a>(pub &'a str);
impl private::Sealed for TransferredNumber<'_> {}
impl FiniteNumber for TransferredNumber<'_> {
fn use_json_number<C: FnOnce(&str) -> Result<(), IoError>>(
&self,
consumer: C,
) -> Result<(), IoError> {
consumer(self.0)
}
fn as_u64(&self) -> Option<u64> {
None
}
fn as_i64(&self) -> Option<i64> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt::Display;
#[test]
fn numbers_strings() {
fn assert_valid_number<T: FiniteNumber + Display>(number: T) {
let mut number_string = String::new();
number
.use_json_number(|json_number| {
number_string.push_str(json_number);
Ok(())
})
.unwrap();
assert_eq!(
true,
is_valid_json_number(&number_string),
"Expected to be valid JSON number: {number}"
);
}
assert_valid_number(i8::MIN);
assert_valid_number(i8::MAX);
assert_valid_number(u8::MAX);
assert_valid_number(i128::MIN);
assert_valid_number(i128::MAX);
assert_valid_number(u128::MAX);
assert_valid_number(isize::MIN);
assert_valid_number(isize::MAX);
assert_valid_number(usize::MAX);
fn assert_valid_fp_number<T: FloatingPointNumber + Display>(number: T) {
let mut number_string = String::new();
number
.use_json_number(|json_number| {
number_string.push_str(json_number);
Ok(())
})
.unwrap();
assert_eq!(
true,
is_valid_json_number(&number_string),
"Expected to be valid JSON number: {number}"
);
}
assert_valid_fp_number(f32::MIN);
assert_valid_fp_number(f32::MIN_POSITIVE);
assert_valid_fp_number(-f32::MIN_POSITIVE);
assert_valid_fp_number(f32::MAX);
assert_valid_fp_number(f64::MIN);
assert_valid_fp_number(f64::MIN_POSITIVE);
assert_valid_fp_number(-f64::MIN_POSITIVE);
assert_valid_fp_number(f64::MAX);
fn assert_non_finite<T: FloatingPointNumber + Display>(number: T) {
match number.use_json_number(|_| panic!("Should have failed for: {number}")) {
Ok(_) => panic!("Should have failed for: {number}"),
Err(e) => match e {
JsonNumberError::InvalidNumber(message) => {
assert_eq!(format!("non-finite number: {number}"), message)
}
JsonNumberError::IoError(e) => panic!("Unexpected error for '{number}': {e:?}"),
},
}
}
assert_non_finite(f32::NAN);
assert_non_finite(f32::NEG_INFINITY);
assert_non_finite(f32::INFINITY);
assert_non_finite(f64::NAN);
assert_non_finite(f64::NEG_INFINITY);
assert_non_finite(f64::INFINITY);
}
}