use core::{fmt::Write, ops::Deref};
use crate::{DatumAtom, DatumToken, DatumTokenType};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DatumWriterState {
None,
QueuedIndent,
AfterToken,
}
impl Default for DatumWriterState {
fn default() -> Self {
Self::None
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct DatumWriter {
pub indent: usize,
pub state: DatumWriterState,
}
impl DatumWriter {
pub fn emit_whitespace(&mut self, f: &mut dyn Write, list_end: bool) -> core::fmt::Result {
match self.state {
DatumWriterState::None => {}
DatumWriterState::QueuedIndent => {
let mut counter = self.indent;
while counter > 0 {
f.write_char('\t')?;
counter -= 1;
}
}
DatumWriterState::AfterToken => {
if !list_end {
f.write_char(' ')?;
}
}
}
self.state = DatumWriterState::None;
Ok(())
}
pub fn write_newline(&mut self, f: &mut dyn Write) -> core::fmt::Result {
self.state = DatumWriterState::QueuedIndent;
f.write_char('\n')
}
pub fn write_comment(&mut self, f: &mut dyn Write, text: &str) -> core::fmt::Result {
self.emit_whitespace(f, false)?;
f.write_str("; ")?;
for v in text.chars() {
if v == '\n' {
self.write_newline(f)?;
self.emit_whitespace(f, false)?;
f.write_str("; ")?;
} else {
f.write_char(v)?;
}
}
self.write_newline(f)
}
pub fn write_token<B: Deref<Target = str>>(
&mut self,
f: &mut dyn Write,
token: &DatumToken<B>,
) -> core::fmt::Result {
let token_type = token.token_type();
self.emit_whitespace(f, token_type == DatumTokenType::ListEnd)?;
token.write(f)?;
if token_type != DatumTokenType::ListStart {
self.state = DatumWriterState::AfterToken;
} else {
self.state = DatumWriterState::None;
}
Ok(())
}
pub fn write_atom<B: Deref<Target = str>>(
&mut self,
f: &mut dyn Write,
value: &DatumAtom<B>,
) -> core::fmt::Result {
self.emit_whitespace(f, false)?;
value.write(f)?;
self.state = DatumWriterState::AfterToken;
Ok(())
}
}