use alloc::string::String;
use alloc::vec::Vec;
use deser_core::hints::Layout;
use deser_core::ser::EventSink;
use deser_core::ser::SerializeRef;
use deser_core::{Atom, Error, ErrorKind, Event, State};
use crate::ser::{Indent, Output};
struct Frame {
is_map: bool,
multiline: bool,
first: bool,
}
struct Attempt {
start: usize,
column: usize,
checked: usize,
}
pub(crate) struct PrettyWriter {
ser: Output,
indent: Indent,
compact: bool,
inline_width: Option<usize>,
stack: Vec<Frame>,
is_key: bool,
line_start: usize,
line_offset: usize,
attempt: Option<Attempt>,
entries: Vec<usize>,
scratch: String,
pub(crate) limit: usize,
}
impl PrettyWriter {
pub(crate) fn new(
ser: Output,
indent: Indent,
compact: bool,
inline_width: Option<usize>,
) -> PrettyWriter {
PrettyWriter {
line_start: ser.out.len(),
ser,
indent,
compact,
inline_width,
stack: Vec::new(),
is_key: false,
line_offset: 0,
attempt: None,
entries: Vec::new(),
scratch: String::new(),
limit: usize::MAX,
}
}
pub(crate) fn finish(self) -> String {
self.ser.out.into_string()
}
pub(crate) fn take_output(&mut self) -> Vec<u8> {
debug_assert!(self.attempt.is_none());
self.line_offset += self.ser.out.as_str()[self.line_start..].chars().count();
self.line_start = 0;
self.ser.out.take()
}
pub(crate) fn output(&mut self) -> &mut crate::buf::Buffer {
&mut self.ser.out
}
pub(crate) fn event(&mut self, event: Event, state: &State) -> Result<(), Error> {
match event {
Event::Atom(atom) => self.atom(atom),
Event::MapStart(_) => self.start(true, Layout::of(state)),
Event::SeqStart(_) => self.start(false, Layout::of(state)),
Event::MapEnd => self.end(true),
Event::SeqEnd => self.end(false),
}
}
fn atom(&mut self, atom: Atom) -> Result<(), Error> {
if self.is_key {
self.begin_entry();
self.ser.write_key_text(atom)?;
self.ser.write_str(if self.compact { ":" } else { ": " });
self.is_key = false;
} else {
if self.stack.last().is_some_and(|frame| !frame.is_map) {
self.begin_entry();
}
self.ser.write_atom(atom)?;
self.complete();
}
if let Some(width) = self.inline_width
&& self.attempt.is_some()
&& self.column() > width
{
self.abort_attempt();
}
Ok(())
}
fn start(&mut self, is_map: bool, layout: Layout) -> Result<(), Error> {
if self.is_key {
return Err(Error::new(
ErrorKind::UnsupportedType,
"JSON does not support this value for map keys",
));
}
if self.attempt.is_some() {
self.abort_attempt();
}
let parent_multiline = match self.stack.last() {
Some(frame) => {
let multiline = frame.multiline;
if !frame.is_map {
self.begin_entry();
}
multiline
}
None => true,
};
let start = self.ser.out.len();
self.ser.write_char(if is_map { '{' } else { '[' });
let mut multiline =
parent_multiline && self.indent != Indent::None && layout != Layout::Compact;
if multiline && layout == Layout::Auto && self.inline_width.is_some() {
multiline = false;
self.entries.clear();
self.attempt = Some(Attempt {
start,
column: self.line_offset
+ self.ser.out.as_str()[self.line_start..start]
.chars()
.count(),
checked: start,
});
}
self.stack.push(Frame {
is_map,
multiline,
first: true,
});
self.is_key = is_map;
Ok(())
}
fn end(&mut self, is_map: bool) -> Result<(), Error> {
match self.stack.last() {
Some(frame) if frame.is_map == is_map && (!is_map || self.is_key) => {}
_ if is_map => return Err(Error::new(ErrorKind::InvalidState, "unexpected map end")),
_ => return Err(Error::new(ErrorKind::InvalidState, "unexpected array end")),
}
if let Some(width) = self.inline_width
&& self.attempt.is_some()
&& self.column() + 1 > width
{
self.abort_attempt();
}
let frame = self.stack.pop().unwrap();
if frame.multiline && !frame.first {
self.newline();
}
self.ser.write_char(if is_map { '}' } else { ']' });
self.attempt = None;
self.complete();
Ok(())
}
fn begin_entry(&mut self) {
let frame = self.stack.last_mut().unwrap();
let first = core::mem::replace(&mut frame.first, false);
if !first {
self.ser.write_char(',');
}
if frame.multiline {
self.newline();
} else if !first && !self.compact {
self.ser.write_char(' ');
}
if self.attempt.is_some() {
self.entries.push(self.ser.out.len());
}
}
fn column(&mut self) -> usize {
let attempt = self.attempt.as_mut().unwrap();
attempt.column += self.ser.out.as_str()[attempt.checked..].chars().count();
attempt.checked = self.ser.out.len();
attempt.column
}
#[cold]
fn abort_attempt(&mut self) {
let attempt = self.attempt.take().unwrap();
let mut text = core::mem::take(&mut self.scratch);
text.clear();
text.push_str(&self.ser.out.as_str()[attempt.start..]);
self.ser.out.truncate(attempt.start);
self.stack.last_mut().unwrap().multiline = true;
self.ser.write_str(&text[..1]);
let separator = if self.compact { 1 } else { 2 };
let entries = core::mem::take(&mut self.entries);
for (idx, &entry) in entries.iter().enumerate() {
if idx > 0 {
self.ser.write_char(',');
}
self.newline();
let end = entries
.get(idx + 1)
.map_or(text.len(), |next| next - attempt.start - separator);
self.ser.write_str(&text[entry - attempt.start..end]);
}
self.entries = entries;
self.scratch = text;
}
fn complete(&mut self) {
self.is_key = self.stack.last().is_some_and(|frame| frame.is_map);
}
fn newline(&mut self) {
const SPACES: &str = " ";
const TABS: &str = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
self.ser.write_char('\n');
self.line_start = self.ser.out.len();
self.line_offset = 0;
let (chunk, mut len) = match self.indent {
Indent::Spaces(width) => (SPACES, width * self.stack.len()),
Indent::Tab => (TABS, self.stack.len()),
Indent::None => return,
};
while len > 0 {
let n = len.min(chunk.len());
self.ser.write_str(&chunk[..n]);
len -= n;
}
}
}
impl EventSink for PrettyWriter {
#[inline]
fn event(
&mut self,
event: Event<'_>,
_value: SerializeRef<'_>,
state: &mut State,
) -> Result<(), Error> {
PrettyWriter::event(self, event, state)
}
#[inline]
fn pause(&mut self) -> bool {
self.attempt.is_none() && self.ser.out.len() >= self.limit
}
}