use std::cell::RefCell;
use std::io;
use std::io::Write;
use qubit_budget::ResourceQuantity;
use super::json_output_accounting::JsonOutputAccounting;
use crate::encode::JsonEncodeError;
pub(in crate::encode) struct JsonOutputBuffer<'a, R, Q>
where
Q: ResourceQuantity,
{
bytes: Vec<u8>,
accounting: &'a RefCell<JsonOutputAccounting<'a, R, Q>>,
remaining: Option<Q>,
}
impl<'a, R, Q> JsonOutputBuffer<'a, R, Q>
where
Q: ResourceQuantity,
{
#[inline]
pub(in crate::encode) fn new(accounting: &'a RefCell<JsonOutputAccounting<'a, R, Q>>) -> Self {
Self {
bytes: Vec::new(),
accounting,
remaining: accounting.borrow().remaining(),
}
}
pub(in crate::encode) fn into_result(
self,
result: Result<(), serde_json::Error>,
) -> Result<Vec<u8>, JsonEncodeError<R, Q>> {
let violation = self.accounting.borrow_mut().take_violation();
if let Some(error) = violation {
return Err(JsonEncodeError::<R, Q>::budget(error));
}
let syntax_error = self.accounting.borrow_mut().take_syntax_error();
if let Some(error) = syntax_error {
return Err(JsonEncodeError::<R, Q>::invalid_raw_json(error));
}
if result.is_err() {
let error = self.accounting.borrow_mut().take_serialization_error_or_custom();
return Err(JsonEncodeError::<R, Q>::serialization(error));
}
Ok(self.bytes)
}
}
impl<R, Q> Write for JsonOutputBuffer<'_, R, Q>
where
R: Clone,
Q: ResourceQuantity,
{
fn write(&mut self, input: &[u8]) -> io::Result<usize> {
let next = match self.bytes.len().checked_add(input.len()) {
Some(next) => next,
None => {
return Err(io::Error::other("JSON output length overflow"));
}
};
if let Some(remaining) = self.remaining {
let amount = Q::try_from_usize(input.len());
match amount {
Ok(amount) if amount <= remaining => {
self.remaining = Some(remaining - amount);
}
Ok(_) | Err(_) => {
let error = self
.accounting
.borrow()
.check_available(next)
.expect_err("the local output capacity already rejected this write");
self.accounting.borrow_mut().record_violation(error);
return Err(io::Error::other("JSON output budget exceeded"));
}
}
}
self.bytes.extend_from_slice(input);
Ok(input.len())
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}