use std::fmt;
use std::fmt::Write as FmtWrite;
use qubit_json::value::DuplicateKeyRejectingJsonValue;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
#[cfg(feature = "converter")]
use serde_json::Map;
use serde_json::Number;
use serde_json::Value;
use super::internal::CanonicalJson;
#[cfg(all(feature = "converter", feature = "json"))]
use super::internal::CanonicalJsonFrame;
#[cfg(feature = "json")]
use super::internal::FixedNumberText;
use super::internal::JsonNumberChildren;
#[cfg(feature = "json")]
use super::internal::NumberTextComparator;
use crate::value_wire::ValueWireEncodeError;
#[inline]
pub(crate) fn serialize<S>(value: &Value, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
CanonicalJson(value).serialize(serializer)
}
#[inline]
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Value, D::Error>
where
D: Deserializer<'de>,
{
DuplicateKeyRejectingJsonValue::deserialize(deserializer).map(DuplicateKeyRejectingJsonValue::into_inner)
}
pub(crate) fn validate_number(number: &Number) -> Result<(), ValueWireEncodeError> {
if let Some(value) = number.as_i64()
&& number_matches_integer(number, value)
{
return Ok(());
}
if let Some(value) = number.as_u64()
&& number_matches_integer(number, value)
{
return Ok(());
}
let mut text = FixedNumberText::new();
if write!(&mut text, "{number}").is_ok() {
validate_number_text(number, text.as_str())
} else {
validate_number_text(number, &number.to_string())
}
}
fn validate_number_text(number: &Number, text: &str) -> Result<(), ValueWireEncodeError> {
let canonical = if text == "-0" {
Number::from_f64(-0.0)
} else if text.bytes().any(|byte| matches!(byte, b'.' | b'e' | b'E')) {
text.parse::<f64>().ok().and_then(Number::from_f64)
} else if text.starts_with('-') {
text.parse::<i64>().ok().map(Number::from)
} else {
text.parse::<u64>().ok().map(Number::from)
};
if canonical.as_ref() == Some(number) {
Ok(())
} else {
Err(ValueWireEncodeError::JsonNumberNotRoundTrippable)
}
}
#[must_use]
fn number_matches_integer<T: fmt::Display>(number: &Number, integer: T) -> bool {
let mut expected = FixedNumberText::new();
if write!(&mut expected, "{integer}").is_err() {
return false;
}
let mut comparator = NumberTextComparator::new(expected.as_bytes());
write!(&mut comparator, "{number}").is_ok() && comparator.is_equal()
}
pub(crate) fn validate_numbers(value: &Value) -> Result<(), ValueWireEncodeError> {
let mut frames = Vec::new();
let mut next = Some(value);
while next.is_some() || !frames.is_empty() {
if let Some(node) = next.take() {
match node {
Value::Number(number) => validate_number(number)?,
Value::Array(_) | Value::Object(_) => {
frames.push(JsonNumberChildren::new(node).expect("container has child iterator"));
}
_ => {}
}
}
while let Some(frame) = frames.last_mut() {
if let Some(child) = frame.next() {
next = Some(child);
break;
}
frames.pop();
}
}
Ok(())
}
#[must_use]
#[cfg(feature = "converter")]
pub(crate) fn canonicalize_json_value(value: &Value) -> Value {
let mut frames = Vec::new();
let mut next = Some(value);
let mut completed = None;
loop {
if let Some(node) = next.take() {
if let Some(mut frame) = CanonicalJsonFrame::new(node) {
next = frame.next_child();
frames.push(frame);
continue;
}
completed = Some(match node {
Value::Array(_) => Value::Array(Vec::new()),
Value::Object(_) => Value::Object(Map::new()),
_ => node.clone(),
});
}
if let Some(child) = completed.take() {
if let Some(parent) = frames.last_mut() {
parent.push(child);
} else {
return child;
}
}
let Some(frame) = frames.last_mut() else {
unreachable!("the root value is returned when its frame completes");
};
if let Some(child) = frame.next_child() {
match child {
Value::Array(_) | Value::Object(_) => next = Some(child),
_ => frame.push(child.clone()),
}
} else {
completed = Some(frames.pop().expect("last frame exists").finish());
}
}
}