use std::fmt::Write;
use crate::{AttributeName, AttributeValue, escapes::StringEscaper, Attributes};
struct TagOpeningData<'a, 't, W: Write> {
tag: &'t str,
w: &'a mut W,
compactability: Compactability,
}
pub struct TagOpening<'a, 't, W: Write> {
data: Option<TagOpeningData<'a, 't, W>>,
}
impl<'a, 't, W: Write> TagOpening<'a, 't, W> {
pub fn new(tag: &'t str, w: &'a mut W, compactability: Compactability) -> Result<Self, std::fmt::Error> {
w.write_str("<")?;
w.write_str(tag)?;
Ok(Self { data: Some(TagOpeningData { tag, w, compactability }) })
}
pub fn attr<'s>(
&'s mut self,
name: impl AttributeName,
value: impl AttributeValue
) -> Result<&'s mut Self, std::fmt::Error> {
let data = self.data.as_mut().unwrap();
data.w.write_str(" ").unwrap();
assert!(name.is_valid_attribute_name());
name.write_attribute_name(data.w)?;
if value.is_unit() {
return Ok(self);
} else {
data.w.write_str("=\"")?;
value.write_attribute_value(&mut StringEscaper::new(data.w))?;
data.w.write_str("\"")?;
}
Ok(self)
}
pub fn with_attr(mut self, name: impl AttributeName, value: impl AttributeValue) -> Result<Self, std::fmt::Error> {
self.attr(name, value)?;
Ok(self)
}
pub fn with_attributes(mut self, attributes: impl Attributes) -> Result<Self, std::fmt::Error> {
attributes.write_attributes(&mut self)?;
Ok(self)
}
pub fn inner_html(mut self) -> Result<InsideTagHtml<'a, 't, W>, std::fmt::Error> {
let data = self.data.take().unwrap();
data.w.write_str(">")?;
Ok(InsideTagHtml { tag: data.tag, w: data.w })
}
}
impl<'a, 't, W: Write> Drop for TagOpening<'a, 't, W> {
fn drop(&mut self) {
if let Some(data) = self.data.take() {
if let Compactability::Yes { final_slash } = data.compactability {
let _ = data.w.write_str(if final_slash { "/>" } else { ">" });
} else {
let _ = data.w.write_fmt(format_args!("></{}>", data.tag));
}
}
}
}
pub struct InsideTagHtml<'a, 't, W: Write> {
tag: &'t str,
w: &'a mut W,
}
impl<'a, 't, W: Write> InsideTagHtml<'a, 't, W> {
}
impl<'a, 't, W: Write> Drop for InsideTagHtml<'a, 't, W> {
fn drop(&mut self) {
let _ = self.w.write_str("</");
let _ = self.w.write_str(self.tag);
let _ = self.w.write_str(">");
}
}
impl<'a, 't, W: Write> Write for InsideTagHtml<'a, 't, W> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.w.write_str(s)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Compactability {
No,
Yes {
final_slash: bool
},
}
impl Compactability {
pub fn is_compactable(&self) -> bool {
match self {
Compactability::No => false,
Compactability::Yes { .. } => true,
}
}
}
impl From<bool> for Compactability {
fn from(b: bool) -> Self {
if b {
Compactability::Yes { final_slash: true }
} else {
Compactability::No
}
}
}
impl From<Compactability> for bool {
fn from(c: Compactability) -> Self {
match c {
Compactability::No => false,
Compactability::Yes { .. } => true,
}
}
}