use crate::{InternalString, RantList, RantMap, RantValue, format::{NumberFormat, OutputFormat}, RantTuple};
use super::format::{WhitespaceNormalizationMode};
use std::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.number_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::Nothing) {
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::Tuple(_)) => {
self.mode = OutputPrintMode::Tuple;
}
OutputBuffer::Value(RantValue::Map(_)) => {
self.mode = OutputPrintMode::Map;
},
_ => {},
}
},
(_, OutputPrintMode::Single | OutputPrintMode::Concat) => {
match &value {
OutputBuffer::Fragment(_) | OutputBuffer::Whitespace(_) | OutputBuffer::Value(RantValue::String(_)) => {
self.mode = OutputPrintMode::Text;
},
_ => {
self.mode = OutputPrintMode::Concat;
}
}
},
(_, OutputPrintMode::List) => {
if !matches!(value, OutputBuffer::Whitespace(_) | OutputBuffer::Value(RantValue::List(_) | RantValue::Tuple(_))) {
self.mode = OutputPrintMode::Text;
}
},
(_, OutputPrintMode::Tuple) => {
match value {
OutputBuffer::Whitespace(_) | OutputBuffer::Value(RantValue::Tuple(_)) => {},
OutputBuffer::Value(RantValue::List(_)) => self.mode = OutputPrintMode::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.whitespace_format {
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::Nothing,
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::Nothing,
}
},
_ => {
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::Nothing
}
},
OutputPrintMode::List => {
let mut output = RantList::new();
for buf in self.buffers {
match buf {
OutputBuffer::Value(RantValue::List(list)) => {
output.extend(list.borrow().iter().cloned());
},
OutputBuffer::Value(RantValue::Tuple(tuple)) => {
output.extend(tuple.iter().cloned());
},
_ => {}
}
}
RantValue::List(output.into_handle())
},
OutputPrintMode::Tuple => {
let mut output: Vec<RantValue> = vec![];
for buf in self.buffers {
if let OutputBuffer::Value(RantValue::Tuple(tuple)) = buf {
output.extend(tuple.iter().cloned());
}
}
RantValue::Tuple(RantTuple::from(output).into_handle())
},
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(output.into_handle())
},
OutputPrintMode::Concat => {
let mut val = RantValue::Nothing;
for buf in self.buffers {
if let OutputBuffer::Value(bufval) = buf {
val = val + bufval;
}
}
val
}
}
}
}
}
}
impl Default for OutputWriter {
fn default() -> Self {
OutputWriter::new(Default::default())
}
}
#[derive(Debug, Copy, Clone)]
enum OutputPrintMode {
Single,
Text,
List,
Tuple,
Map,
Concat,
}
#[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 {
Self::Fragment(s) => s,
Self::Whitespace(s) => s,
Self::Value(RantValue::Nothing) => return None,
Self::Value(RantValue::Int(n)) => format.number_format.format_integer(n),
Self::Value(RantValue::Float(n)) => format.number_format.format_float(n),
Self::Value(v) => InternalString::from(v.to_string()),
Self::NumberFormatUpdate(fmt) => {
format.number_format = fmt;
return None
},
})
}
}