use std::cell::RefCell;
use std::fmt::Display;
use qubit_budget::ResourceQuantity;
use qubit_budget::json::JsonContainerKind;
use qubit_budget::json::JsonMeasurement;
use serde::Serialize;
use serde::Serializer;
use serde::ser::Error;
use super::super::serde_compat::PrivateStructKind;
use super::super::serde_compat::SerdeJsonCompat;
use super::budgeted_value::BudgetedValue;
use super::display_budget_kind::DisplayBudgetKind;
use super::json_encode_compound::JsonEncodeCompound;
use super::json_encode_context::JsonEncodeContext;
use crate::encode::JsonIntegerSignedness;
use crate::encode::JsonSerializationErrorKind;
use crate::internal::JsonLexemeLength;
pub(in crate::encode) struct JsonEncodeSerializer<'transaction, 'budget, 'context, S, R, Q, const VALUE_LIMITS: bool>
where
Q: ResourceQuantity,
{
inner: S,
context: &'context RefCell<JsonEncodeContext<'transaction, 'budget, R, Q>>,
depth: usize,
}
impl<'transaction, 'budget, 'context, S, R, Q, const VALUE_LIMITS: bool>
JsonEncodeSerializer<'transaction, 'budget, 'context, S, R, Q, VALUE_LIMITS>
where
R: Clone,
Q: ResourceQuantity,
{
#[inline]
pub(in crate::encode) fn new(
inner: S,
context: &'context RefCell<JsonEncodeContext<'transaction, 'budget, R, Q>>,
) -> Self {
Self {
inner,
context,
depth: 1,
}
}
pub(super) const fn with_context(
inner: S,
context: &'context RefCell<JsonEncodeContext<'transaction, 'budget, R, Q>>,
depth: usize,
) -> Self {
Self { inner, context, depth }
}
#[inline(always)]
fn admit<E>(&self, measurement: JsonMeasurement) -> Result<(), E>
where
E: Error,
{
if !VALUE_LIMITS {
return Ok(());
}
self.context.borrow_mut().admit(measurement)
}
#[inline(always)]
fn enter_container<E>(&self, kind: JsonContainerKind, depth: usize) -> Result<(), E>
where
E: Error,
{
if !VALUE_LIMITS {
return Ok(());
}
self.context.borrow_mut().enter_container(kind, depth)
}
#[inline(always)]
fn string<E>(&self, bytes: usize) -> Result<(), E>
where
E: Error,
{
self.admit(JsonMeasurement::String {
depth: self.depth,
bytes,
})
}
#[inline(always)]
fn number<E>(&self, bytes: usize) -> Result<(), E>
where
E: Error,
{
self.admit(JsonMeasurement::Number {
depth: self.depth,
bytes,
})
}
#[inline(always)]
fn array<E>(&self, depth: usize, items: usize) -> Result<(), E>
where
E: Error,
{
self.admit(JsonMeasurement::Array { depth, items })
}
#[inline(always)]
fn object<E>(&self, depth: usize, entries: usize) -> Result<(), E>
where
E: Error,
{
self.admit(JsonMeasurement::Object { depth, entries })
}
#[inline(always)]
fn key<E>(&self, key: &str) -> Result<(), E>
where
E: Error,
{
self.admit(JsonMeasurement::Key { bytes: key.len() })
}
#[inline]
fn serialization_error<E>(&self, kind: JsonSerializationErrorKind) -> E
where
E: Error,
{
self.context.borrow_mut().serialization_error(kind)
}
}
macro_rules! serialize_integer {
($name:ident, signed $type:ty) => {
#[doc = concat!("Charges and delegates one signed `", stringify!($type), "` JSON integer.")]
#[inline(always)]
fn $name(self, value: $type) -> Result<Self::Ok, Self::Error> {
if VALUE_LIMITS {
self.number(JsonLexemeLength::signed_integer(value.into()))?;
}
self.inner.$name(value)
}
};
($name:ident, unsigned $type:ty) => {
#[doc = concat!("Charges and delegates one unsigned `", stringify!($type), "` JSON integer.")]
#[inline(always)]
fn $name(self, value: $type) -> Result<Self::Ok, Self::Error> {
if VALUE_LIMITS {
self.number(JsonLexemeLength::unsigned_integer(value.into()))?;
}
self.inner.$name(value)
}
};
}
impl<'transaction, 'budget, 'context, S, R, Q, const VALUE_LIMITS: bool> Serializer
for JsonEncodeSerializer<'transaction, 'budget, 'context, S, R, Q, VALUE_LIMITS>
where
S: Serializer,
R: Clone,
Q: ResourceQuantity,
{
type Ok = S::Ok;
type Error = S::Error;
type SerializeSeq = JsonEncodeCompound<'transaction, 'budget, 'context, S::SerializeSeq, R, Q, VALUE_LIMITS>;
type SerializeTuple = JsonEncodeCompound<'transaction, 'budget, 'context, S::SerializeTuple, R, Q, VALUE_LIMITS>;
type SerializeTupleStruct =
JsonEncodeCompound<'transaction, 'budget, 'context, S::SerializeTupleStruct, R, Q, VALUE_LIMITS>;
type SerializeTupleVariant =
JsonEncodeCompound<'transaction, 'budget, 'context, S::SerializeTupleVariant, R, Q, VALUE_LIMITS>;
type SerializeMap = JsonEncodeCompound<'transaction, 'budget, 'context, S::SerializeMap, R, Q, VALUE_LIMITS>;
type SerializeStruct = JsonEncodeCompound<'transaction, 'budget, 'context, S::SerializeStruct, R, Q, VALUE_LIMITS>;
type SerializeStructVariant =
JsonEncodeCompound<'transaction, 'budget, 'context, S::SerializeStructVariant, R, Q, VALUE_LIMITS>;
#[inline(always)]
fn serialize_bool(self, value: bool) -> Result<Self::Ok, Self::Error> {
self.admit(JsonMeasurement::Boolean { depth: self.depth })?;
self.inner.serialize_bool(value)
}
serialize_integer!(serialize_i8, signed i8);
serialize_integer!(serialize_i16, signed i16);
serialize_integer!(serialize_i32, signed i32);
serialize_integer!(serialize_i64, signed i64);
serialize_integer!(serialize_u8, unsigned u8);
serialize_integer!(serialize_u16, unsigned u16);
serialize_integer!(serialize_u32, unsigned u32);
serialize_integer!(serialize_u64, unsigned u64);
fn serialize_i128(self, value: i128) -> Result<Self::Ok, Self::Error> {
let bytes = if let Ok(value) = i64::try_from(value) {
JsonLexemeLength::signed_integer(value.into())
} else if let Ok(value) = u64::try_from(value) {
JsonLexemeLength::unsigned_integer(value.into())
} else {
return Err(self.serialization_error(JsonSerializationErrorKind::IntegerOutOfRange {
signedness: JsonIntegerSignedness::Signed,
}));
};
if VALUE_LIMITS {
self.number(bytes)?;
}
self.inner.serialize_i128(value)
}
fn serialize_u128(self, value: u128) -> Result<Self::Ok, Self::Error> {
let value64 = u64::try_from(value).map_err(|_| {
self.serialization_error(JsonSerializationErrorKind::IntegerOutOfRange {
signedness: JsonIntegerSignedness::Unsigned,
})
})?;
if VALUE_LIMITS {
self.number(JsonLexemeLength::unsigned_integer(value64.into()))?;
}
self.inner.serialize_u128(value)
}
fn serialize_f32(self, value: f32) -> Result<Self::Ok, Self::Error> {
if !value.is_finite() {
return Err(self.serialization_error(JsonSerializationErrorKind::NonFiniteFloat));
}
if VALUE_LIMITS {
self.number(JsonLexemeLength::finite_f32(value))?;
}
self.inner.serialize_f32(value)
}
fn serialize_f64(self, value: f64) -> Result<Self::Ok, Self::Error> {
if !value.is_finite() {
return Err(self.serialization_error(JsonSerializationErrorKind::NonFiniteFloat));
}
if VALUE_LIMITS {
self.number(JsonLexemeLength::finite_f64(value))?;
}
self.inner.serialize_f64(value)
}
fn serialize_char(self, value: char) -> Result<Self::Ok, Self::Error> {
self.string(value.len_utf8())?;
self.inner.serialize_char(value)
}
#[inline(always)]
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
self.string(value.len())?;
self.inner.serialize_str(value)
}
fn serialize_bytes(self, value: &[u8]) -> Result<Self::Ok, Self::Error> {
if !VALUE_LIMITS {
return self.inner.serialize_bytes(value);
}
self.array(self.depth, value.len())?;
let child_depth = self.depth.saturating_add(1);
for byte in value {
self.admit(JsonMeasurement::Number {
depth: child_depth,
bytes: JsonLexemeLength::byte(*byte),
})?;
}
self.inner.serialize_bytes(value)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.admit(JsonMeasurement::Null { depth: self.depth })?;
self.inner.serialize_none()
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: Serialize + ?Sized,
{
let value = BudgetedValue::<_, _, _, VALUE_LIMITS>::new(value, self.context, self.depth);
self.inner.serialize_some(&value)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
self.admit(JsonMeasurement::Null { depth: self.depth })?;
self.inner.serialize_unit()
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
self.admit(JsonMeasurement::Null { depth: self.depth })?;
self.inner.serialize_unit_struct(name)
}
fn serialize_unit_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
self.string(variant.len())?;
self.inner.serialize_unit_variant(name, variant_index, variant)
}
fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<Self::Ok, Self::Error>
where
T: Serialize + ?Sized,
{
let value = BudgetedValue::<_, _, _, VALUE_LIMITS>::new(value, self.context, self.depth);
self.inner.serialize_newtype_struct(name, &value)
}
fn serialize_newtype_variant<T>(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize + ?Sized,
{
self.object(self.depth, 1)?;
self.key(variant)?;
let value = BudgetedValue::<_, _, _, VALUE_LIMITS>::new(value, self.context, self.depth.saturating_add(1));
self.inner
.serialize_newtype_variant(name, variant_index, variant, &value)
}
#[inline(always)]
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
let context = self.context;
self.enter_container(JsonContainerKind::Sequence, self.depth)?;
let child_depth = self.depth.saturating_add(1);
let inner = self.inner.serialize_seq(len)?;
Ok(JsonEncodeCompound::new(inner, context, child_depth))
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
let context = self.context;
self.enter_container(JsonContainerKind::Sequence, self.depth)?;
let child_depth = self.depth.saturating_add(1);
let inner = self.inner.serialize_tuple(len)?;
Ok(JsonEncodeCompound::new(inner, context, child_depth))
}
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct, Self::Error> {
let context = self.context;
self.enter_container(JsonContainerKind::Sequence, self.depth)?;
let child_depth = self.depth.saturating_add(1);
let inner = self.inner.serialize_tuple_struct(name, len)?;
Ok(JsonEncodeCompound::new(inner, context, child_depth))
}
fn serialize_tuple_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
self.object(self.depth, 1)?;
self.key(variant)?;
let array_depth = self.depth.saturating_add(1);
self.enter_container(JsonContainerKind::Sequence, array_depth)?;
let context = self.context;
let child_depth = array_depth.saturating_add(1);
let inner = self.inner.serialize_tuple_variant(name, variant_index, variant, len)?;
Ok(JsonEncodeCompound::new(inner, context, child_depth))
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
let context = self.context;
self.enter_container(JsonContainerKind::Map, self.depth)?;
let child_depth = self.depth.saturating_add(1);
let inner = self.inner.serialize_map(len)?;
Ok(JsonEncodeCompound::new(inner, context, child_depth))
}
#[inline(always)]
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct, Self::Error> {
match SerdeJsonCompat::classify_private_struct(name) {
Some(PrivateStructKind::RawValue) => {
let context = self.context;
let depth = self.depth;
let inner = self.inner.serialize_struct(name, len)?;
Ok(JsonEncodeCompound::raw_value(inner, context, depth))
}
None => {
let context = self.context;
self.enter_container(JsonContainerKind::Map, self.depth)?;
let child_depth = self.depth.saturating_add(1);
let inner = self.inner.serialize_struct(name, len)?;
Ok(JsonEncodeCompound::new(inner, context, child_depth))
}
}
}
fn serialize_struct_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
self.object(self.depth, 1)?;
self.key(variant)?;
let object_depth = self.depth.saturating_add(1);
self.enter_container(JsonContainerKind::Map, object_depth)?;
let context = self.context;
let child_depth = object_depth.saturating_add(1);
let inner = self.inner.serialize_struct_variant(name, variant_index, variant, len)?;
Ok(JsonEncodeCompound::new(inner, context, child_depth))
}
fn collect_str<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: Display + ?Sized,
{
let text = JsonEncodeContext::collect_display::<S::Error, _>(
self.context,
value,
DisplayBudgetKind::String,
self.depth,
)?;
self.inner.serialize_str(&text)
}
#[inline(always)]
fn is_human_readable(&self) -> bool {
self.inner.is_human_readable()
}
}