use crate::{InternalString, RantList, RantMap, RantValue, format::{NumberFormat, OutputFormat}};
use super::format::{WhitespaceNormalizationMode};
use std::{cell::RefCell, rc::Rc};
const INITIAL_CHAIN_CAPACITY: usize = 64;
const DEFAULT_SPACE: &str = " ";
pub struct OutputWriter {
buffers: Vec<OutputBuffer>,
format: Rc<OutputFormat>,
mode: OutputPrintMode,
}
impl OutputWriter {
#[inline]
pub fn new(prev_output: Option<&Self>) -> Self {
Self {
buffers: Vec::with_capacity(INITIAL_CHAIN_CAPACITY),
format: prev_output.map(|o| Rc::clone(&o.format)).unwrap_or_default(),
mode: OutputPrintMode::Single,
}
}
#[inline]
pub fn format(&self) -> &OutputFormat {
&self.format
}
#[inline]
pub fn format_mut(&mut self) -> &mut OutputFormat {
Rc::make_mut(&mut self.format)
}
#[inline]
fn last_buffer(&self) -> Option<&OutputBuffer> {
self.buffers.last()
}
#[inline]
fn last_buffer_mut(&mut self) -> Option<&mut OutputBuffer> {
self.buffers.last_mut()
}
#[inline]
pub fn update_number_format(&mut self) {
let fmt = self.format.num_format.clone();
if let Some(OutputBuffer::NumberFormatUpdate(upd)) = self.last_buffer_mut() {
*upd = fmt;
} else {
self.write_buffer(OutputBuffer::NumberFormatUpdate(fmt));
}
}
#[inline]
pub fn write_value(&mut self, value: RantValue) {
if !matches!(value, RantValue::Empty) {
self.write_buffer(OutputBuffer::Value(value));
}
}
#[inline]
fn write_buffer(&mut self, value: OutputBuffer) {
match (self.buffers.len() + 1, self.mode) {
(1, _) => {
match &value {
OutputBuffer::Fragment(_) => {
self.mode = OutputPrintMode::Text;
},
OutputBuffer::Value(RantValue::List(_)) => {
self.mode = OutputPrintMode::List;
},
OutputBuffer::Value(RantValue::Map(_)) => {
self.mode = OutputPrintMode::Map;
},
_ => {},
}
},
(_, OutputPrintMode::Single) => {
self.mode = OutputPrintMode::Text;
},
(_, OutputPrintMode::List) => {
if !matches!(value, OutputBuffer::Whitespace(_) | OutputBuffer::Value(RantValue::List(_))) {
self.mode = OutputPrintMode::Text;
}
},
(_, OutputPrintMode::Map) => {
if !matches!(value, OutputBuffer::Whitespace(_) | OutputBuffer::Value(RantValue::Map(_))) {
self.mode = OutputPrintMode::Text;
}
},
_ => {}
}
self.buffers.push(value);
}
#[inline]
pub fn write_frag(&mut self, value: &str) {
self.write_buffer(OutputBuffer::Fragment(InternalString::from(value)));
}
#[inline]
pub fn write_ws(&mut self, value: &str) {
let ws_str = match &self.format.ws_norm_mode {
WhitespaceNormalizationMode::Default => DEFAULT_SPACE,
WhitespaceNormalizationMode::IgnoreAll => return,
WhitespaceNormalizationMode::Verbatim => value,
WhitespaceNormalizationMode::Custom(val) => {
let val = val.to_string();
self.write_buffer(OutputBuffer::Whitespace(InternalString::from(&val)));
return
},
};
self.write_buffer(OutputBuffer::Whitespace(InternalString::from(ws_str)));
}
}
impl OutputWriter {
#[inline]
pub fn render_value(mut self) -> RantValue {
match self.buffers.len() {
0 => RantValue::Empty,
1 => {
let buffer = self.buffers.pop().unwrap();
match buffer {
OutputBuffer::Fragment(s) | OutputBuffer::Whitespace(s) => RantValue::String(s.as_str().into()),
OutputBuffer::Value(v) => v,
_ => RantValue::Empty,
}
},
_ => {
match self.mode {
OutputPrintMode::Single | OutputPrintMode::Text => {
let mut has_any_nonempty = false;
let mut output = InternalString::new();
let mut format: OutputFormat = Default::default();
for buf in self.buffers {
if let Some(s) = buf.render_string(&mut format) {
has_any_nonempty = true;
output.push_str(s.as_str());
}
}
if has_any_nonempty {
RantValue::String(output.as_str().into())
} else {
RantValue::Empty
}
},
OutputPrintMode::List => {
let mut output = RantList::new();
for buf in self.buffers {
if let OutputBuffer::Value(RantValue::List(list)) = buf {
output.extend(list.borrow().iter().cloned());
}
}
RantValue::List(Rc::new(RefCell::new(output)))
},
OutputPrintMode::Map => {
let mut output = RantMap::new();
for buf in self.buffers {
if let OutputBuffer::Value(RantValue::Map(map)) = buf {
output.extend(map.borrow())
}
}
RantValue::Map(Rc::new(RefCell::new(output)))
},
}
}
}
}
}
impl Default for OutputWriter {
fn default() -> Self {
OutputWriter::new(Default::default())
}
}
#[derive(Debug, Copy, Clone)]
enum OutputPrintMode {
Single,
Text,
List,
Map,
}
#[derive(Debug)]
enum OutputBuffer {
Fragment(InternalString),
Whitespace(InternalString),
Value(RantValue),
NumberFormatUpdate(NumberFormat),
}
impl OutputBuffer {
#[inline]
pub(crate) fn render_string(self, format: &mut OutputFormat) -> Option<InternalString> {
Some(match self {
OutputBuffer::Fragment(s) => s,
OutputBuffer::Whitespace(s) => s,
OutputBuffer::Value(RantValue::Empty) => return None,
OutputBuffer::Value(RantValue::Int(n)) => format.num_format.format_integer(n),
OutputBuffer::Value(RantValue::Float(n)) => format.num_format.format_float(n),
OutputBuffer::Value(v) => InternalString::from(v.to_string()),
OutputBuffer::NumberFormatUpdate(fmt) => {
format.num_format = fmt;
return None
},
})
}
}