use crate::{Result, format_in};
use tsv_lang::doc::arena::DocArena;
pub struct Formatter {
ast: bumpalo::Bump,
doc: DocArena,
}
impl Formatter {
#[must_use]
pub fn new() -> Self {
Self {
ast: bumpalo::Bump::new(),
doc: DocArena::new(),
}
}
pub fn format(&mut self, source: &str) -> Result<String> {
self.ast.reset();
self.doc.reset();
let formatted = format_in(source, &self.ast, |_| &self.doc);
self.ast.reset();
formatted
}
}
impl Default for Formatter {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for Formatter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Formatter").finish_non_exhaustive()
}
}
const _: () = {
const fn assert_send<T: Send>() {}
assert_send::<Formatter>();
};
#[cfg(test)]
mod tests {
use super::Formatter;
use crate::format;
#[test]
fn a_reused_formatter_matches_the_one_shot_function() {
let large = "a { color: red; }\n".repeat(2_000);
let sources = ["a{color:red}", "a {", large.as_str(), "b\r\n{ }", ""];
let mut formatter = Formatter::default();
for source in sources {
let reused = formatter.format(source).map_err(|e| format!("{e:#}"));
let fresh = format(source).map_err(|e| format!("{e:#}"));
assert_eq!(reused, fresh, "{source:?}");
}
}
}