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 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::json_encode_context::JsonEncodeContext;
use super::serializer::json_encode_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<W, T>(&mut self, writer: W, value: &T) -> Result<(), JsonEncodeError<R, Q>>
where
W: Write,
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<W, T>(&mut self, writer: W, value: &T) -> Result<(), JsonEncodeError<R, Q>>
where
W: Write,
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 _ = output.flush();
}
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 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 _ = output.flush();
}
output.into_result(result)
}
fn write_buffer<W>(
writer: W,
bytes: &[u8],
attempt: &mut JsonEncodeAttempt<'_, R, Q>,
) -> Result<(), JsonEncodeError<R, Q>>
where
W: Write,
{
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)
}
}