use std::fmt::Write as _;
pub use adocers_render_core::escape::{
escape_attr,
escape_text,
};
#[derive(Debug, Default)]
pub(crate) struct Buffer {
out: String,
}
impl Buffer {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn raw(&mut self, markup: &str) {
self.out.push_str(markup);
}
pub(crate) fn line(&mut self, markup: &str) {
self.out.push_str(markup);
self.out.push('\n');
}
pub(crate) fn unline(&mut self) {
if self.out.ends_with('\n') {
self.out.pop();
}
}
pub(crate) fn newline(&mut self) {
if !self.out.is_empty() && !self.out.ends_with('\n') {
self.out.push('\n');
}
}
pub(crate) fn open(&mut self, tag: &str, id: Option<&str>, classes: &[&str]) {
let tag = open_tag(tag, id, classes);
self.line(&tag);
}
pub(crate) fn element(&mut self, tag: &str, id: Option<&str>, classes: &[&str], content: &str) {
let open = open_tag(tag, id, classes);
self.line(&format!("{open}{content}</{tag}>"));
}
pub(crate) fn close(&mut self, tag: &str) {
let _ = writeln!(self.out, "</{tag}>");
}
pub(crate) fn finish(self) -> String {
self.out
}
}
fn open_tag(tag: &str, id: Option<&str>, classes: &[&str]) -> String {
open_tag_with(tag, id, classes, "")
}
pub(crate) fn open_tag_with(tag: &str, id: Option<&str>, classes: &[&str], extra: &str) -> String {
let mut out = format!("<{tag}");
if let Some(id) = id {
let _ = write!(out, " id=\"{}\"", escape_attr(id));
}
let classes: Vec<&str> = classes.iter().filter(|c| !c.is_empty()).copied().collect();
if !classes.is_empty() {
let _ = write!(out, " class=\"{}\"", escape_attr(&classes.join(" ")));
}
out.push_str(extra);
out.push('>');
out
}
impl std::fmt::Write for Buffer {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.out.push_str(s);
Ok(())
}
}