use std::cell::RefCell;
use std::fmt::Debug;
use std::io::Write;
use qubit_budget::ResourceQuantity;
use qubit_budget::json::JsonEncodeAttempt;
use qubit_budget::json::JsonEncodeLimits;
use qubit_budget::json::JsonEncodeSession;
use qubit_budget::json::JsonResource;
use qubit_io::ByteOutput;
use serde::Serialize;
use serde_json::Error as JsonError;
use serde_json::Serializer as JsonSerializer;
use super::JsonEncodeError;
use super::output::JsonOutputAccounting;
use super::output::JsonOutputBuffer;
use super::output::JsonOutputWriter;
use super::serializer::JsonEncodeContext;
use super::serializer::JsonEncodeSerializer;
pub struct JsonEncoder<'budget, R = JsonResource, Q = usize>
where
Q: ResourceQuantity,
{
session: JsonEncodeSession<'budget, R, Q>,
}
impl<R, Q> JsonEncoder<'static, R, Q>
where
R: Clone + Debug,
Q: ResourceQuantity,
{
#[inline(always)]
#[must_use]
pub fn with_limits(limits: JsonEncodeLimits<R, Q>) -> Self {
Self::new(JsonEncodeSession::from_limits(limits))
}
}
impl JsonEncoder<'static, JsonResource, usize> {
#[inline(always)]
#[must_use]
pub fn unlimited() -> Self {
Self::with_limits(JsonEncodeLimits::new())
}
}
impl<'budget, R, Q> JsonEncoder<'budget, R, Q>
where
R: Clone + Debug,
Q: ResourceQuantity,
{
#[inline(always)]
#[must_use]
pub fn new(session: JsonEncodeSession<'budget, R, Q>) -> Self {
Self { session }
}
#[inline(always)]
#[must_use]
pub const fn session(&self) -> &JsonEncodeSession<'budget, R, Q> {
&self.session
}
#[inline(always)]
#[must_use]
pub const fn session_mut(&mut self) -> &mut JsonEncodeSession<'budget, R, Q> {
&mut self.session
}
#[inline(always)]
#[must_use]
pub fn into_session(self) -> JsonEncodeSession<'budget, R, Q> {
self.session
}
pub fn to_vec<T>(&mut self, value: &T) -> Result<Vec<u8>, JsonEncodeError<R, Q>>
where
T: Serialize + ?Sized,
{
let has_value_limits = self.session.value_budget().limits().has_limits();
let mut attempt = self.session.begin_value();
let bytes = Self::serialize_buffer(value, &mut attempt, has_value_limits)?;
attempt
.try_consume_output_bytes(bytes.len())
.map_err(JsonEncodeError::<R, Q>::budget)?;
attempt.commit().map_err(JsonEncodeError::<R, Q>::budget)?;
Ok(bytes)
}
pub fn write_buffered<O, T>(&mut self, writer: &mut O, value: &T) -> Result<(), JsonEncodeError<R, Q>>
where
O: ByteOutput + ?Sized,
T: Serialize + ?Sized,
{
let has_value_limits = self.session.value_budget().limits().has_limits();
let mut attempt = self.session.begin_value();
let bytes = Self::serialize_buffer(value, &mut attempt, has_value_limits)?;
attempt
.check_output_bytes(bytes.len())
.map_err(JsonEncodeError::<R, Q>::budget)?;
Self::write_buffer(writer, &bytes, &mut attempt)?;
attempt.commit().map_err(JsonEncodeError::<R, Q>::budget)?;
Ok(())
}
pub fn write_incremental<O, T>(&mut self, writer: &mut O, value: &T) -> Result<(), JsonEncodeError<R, Q>>
where
O: ByteOutput + ?Sized,
T: Serialize + ?Sized,
{
let has_value_limits = self.session.value_budget().limits().has_limits();
let mut attempt = self.session.begin_value();
let result = {
let (output_budget, transaction) = attempt.split_mut();
let accounting = RefCell::new(JsonOutputAccounting::new(output_budget));
let mut output = JsonOutputWriter::new(writer, &accounting);
let result = {
let mut inner = JsonSerializer::new(&mut output);
let context = RefCell::new(JsonEncodeContext {
transaction,
output: &accounting,
has_value_limits,
});
if has_value_limits {
value.serialize(JsonEncodeSerializer::<_, R, Q, true>::new(&mut inner, &context))
} else {
value.serialize(JsonEncodeSerializer::<_, R, Q, false>::new(&mut inner, &context))
}
};
if result.is_ok() {
let _ = Write::flush(&mut output);
}
output.into_result(result)
};
result?;
attempt.commit().map_err(JsonEncodeError::<R, Q>::budget)?;
Ok(())
}
fn serialize_buffer<T>(
value: &T,
attempt: &mut JsonEncodeAttempt<'_, R, Q>,
has_value_limits: bool,
) -> Result<Vec<u8>, JsonEncodeError<R, Q>>
where
T: Serialize + ?Sized,
{
let (output_budget, transaction) = attempt.split_mut();
if output_budget.is_none() {
let accounting = RefCell::new(JsonOutputAccounting::new(None));
let mut bytes = Vec::new();
let result = {
let mut inner = JsonSerializer::new(&mut bytes);
let context = RefCell::new(JsonEncodeContext {
transaction,
output: &accounting,
has_value_limits,
});
if has_value_limits {
value.serialize(JsonEncodeSerializer::<_, R, Q, true>::new(&mut inner, &context))
} else {
value.serialize(JsonEncodeSerializer::<_, R, Q, false>::new(&mut inner, &context))
}
};
if let Some(error) = accounting.borrow_mut().take_violation() {
return Err(JsonEncodeError::<R, Q>::budget(error));
}
if let Some(error) = accounting.borrow_mut().take_syntax_error() {
return Err(JsonEncodeError::<R, Q>::invalid_raw_json(error));
}
if let Some(error) = accounting.borrow_mut().take_serialization_error() {
return Err(JsonEncodeError::<R, Q>::serialization(error));
}
if result.is_err() {
let error = accounting.borrow_mut().take_serialization_error_or_custom();
return Err(JsonEncodeError::<R, Q>::serialization(error));
}
return Ok(bytes);
}
let accounting = RefCell::new(JsonOutputAccounting::new(output_budget));
let mut output = JsonOutputBuffer::new(&accounting);
let result = {
let mut inner = JsonSerializer::new(&mut output);
let context = RefCell::new(JsonEncodeContext {
transaction,
output: &accounting,
has_value_limits,
});
if has_value_limits {
value.serialize(JsonEncodeSerializer::<_, R, Q, true>::new(&mut inner, &context))
} else {
value.serialize(JsonEncodeSerializer::<_, R, Q, false>::new(&mut inner, &context))
}
};
if result.is_ok() {
let _ = Write::flush(&mut output);
}
output.into_result(result)
}
fn write_buffer<O>(
writer: &mut O,
bytes: &[u8],
attempt: &mut JsonEncodeAttempt<'_, R, Q>,
) -> Result<(), JsonEncodeError<R, Q>>
where
O: ByteOutput + ?Sized,
{
let (output_budget, _) = attempt.split_mut();
let accounting = RefCell::new(JsonOutputAccounting::new(output_budget));
let mut output = JsonOutputWriter::new(writer, &accounting);
let result = output.write_all(bytes).map_err(JsonError::io);
output.into_result(result)
}
}