use super::*;
use std::collections::BTreeMap;
use std::mem;
#[derive(Debug)]
pub struct Output {
fmt: Formatting,
line: String,
indent: usize,
lines: Vec<String>,
comments: BTreeMap<usize, Vec<String>>,
indents: BTreeMap<usize, usize>,
}
impl Output {
pub fn new(fmt: &Formatting) -> Self {
Self {
fmt: fmt.to_owned(),
line: Default::default(),
indent: Default::default(),
lines: Default::default(),
comments: Default::default(),
indents: Default::default(),
}
}
pub fn write_key_and_separator(&mut self, key: impl ToString) {
if self.fmt.objects_style.surround_keys_with_quotes {
self.write_char('"');
self.write(key);
self.write_char('"');
} else {
self.write(key);
}
self.write_char(':');
self.write_char(' ');
}
pub fn write_property_separator_ln(&mut self) {
if self.fmt.objects_style.use_comma_as_separator {
self.write_char(',');
}
self.ln();
}
pub fn write(&mut self, str: impl ToString) {
for ch in str.to_string().chars() {
self.write_char(ch);
}
}
pub fn writeln(&mut self, str: impl ToString) {
self.write(str);
self.ln();
}
pub fn ln(&mut self) {
self.write_char('\n');
}
pub fn writeln_comment(&mut self, comment: impl ToString) {
let comment = comment.to_string();
for comment in comment.split('\n') {
let comment = if comment.contains('\t') {
comment.replace('\t', &" ".repeat(self.fmt.indent_style.size))
} else {
comment.to_owned()
};
self.comments
.entry(self.lines.len())
.or_default()
.push(comment);
}
}
pub fn append_comment(&mut self, f: impl FnOnce(&mut String)) {
let mut comment = self
.comments
.get_mut(&self.lines.len())
.and_then(|comments| {
if comments.len() == 1 {
comments.pop()
} else {
None
}
})
.unwrap_or_default();
f(&mut comment);
self.writeln_comment(comment);
}
pub fn inc_indent(&mut self) {
self.indent += 1;
}
pub fn dec_indent(&mut self) {
self.indent -= 1;
}
pub fn render(mut self) -> String {
if !self.line.is_empty() {
self.write_char('\n');
}
match self.fmt.layout {
Layout::OneColumn => layouts::one_column::render(&self),
Layout::TwoColumns { align, spacing } => {
layouts::two_columns::render(&self, align, spacing)
}
}
}
fn write_char(&mut self, ch: char) {
match ch {
'\t' => {
self.write(" ".repeat(self.fmt.indent_style.size));
}
'\r' => {
}
'\n' => {
self.lines.push(mem::take(&mut self.line));
}
ch => {
if self.line.is_empty() {
self.indents.insert(self.lines.len(), self.indent);
}
self.line.push(ch);
}
}
}
}
impl<'a> common::Output<'a> for Output {
fn comment_separator(&self) -> &str {
&self.fmt.comments_style.separator
}
fn lines(&'a self) -> Lines<'a> {
Lines::new(
&self.lines,
self.fmt.indent_style.size,
&self.indents,
&self.comments,
)
}
}