use html_escape::encode_double_quoted_attribute_to_string;
pub use html_escape::encode_text_minimal_to_string as escape_inner;
pub trait Child: std::fmt::Display {}
impl<T: 'static> Child for T where T: std::fmt::Display {}
pub struct Tag {
pub name: &'static str,
pub attrs: Vec<(&'static str, Box<dyn std::fmt::Display>)>,
pub children: Vec<Box<dyn Child>>,
}
impl Tag {
pub fn with_children<V: Child + 'static, T: IntoIterator<Item = V>>(
mut self,
children: T,
) -> Self {
self.children.clear();
self.add_children(children)
}
pub fn add_children<V: Child + 'static, T: IntoIterator<Item = V>>(
mut self,
children: T,
) -> Self {
self.children.extend(
children
.into_iter()
.map(|v| -> Box<dyn Child> { Box::new(v) }),
);
self
}
}
impl std::fmt::Display for Tag {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use std::fmt::Write;
write!(f, "<{}", self.name)?;
let mut buf = String::new();
let mut enbuf = String::new();
for (k, v) in self.attrs.iter() {
buf.clear();
write!(&mut buf, "{}", v)?;
if buf.is_empty() {
continue;
}
enbuf.clear();
encode_double_quoted_attribute_to_string(buf.as_str(), &mut enbuf);
write!(f, " {}=\"{}\"", k, enbuf)?;
}
write!(f, ">")?;
for child in self.children.iter() {
write!(f, "{}", child)?;
}
write!(f, "</{}>", self.name)
}
}
#[macro_export]
macro_rules! tag {
($name:literal[$($k:literal=$v:expr)+] $($children:expr)*) => {
$crate::Tag {
name: $name,
attrs: vec![$(($k, Box::new($v))),*],
children: vec![$(Box::new($children)),*],
}
};
($name:literal $($children:expr)*) => {
$crate::Tag {
name: $name,
attrs: vec![],
children: vec![$(Box::new($children)),*],
}
};
}
#[macro_export]
macro_rules! _maybe {
($v:literal) => {
Some($v)
};
($v:expr) => {
$v
};
}
#[macro_export]
macro_rules! class {
($($v:expr),*) => {
(&[$($crate::_maybe!($v)),*]).into_iter().flatten().join(" ")
};
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(
tag!("html" tag!("body" 123)).to_string().as_str(),
"<html><body>123</body></html>"
);
assert_eq!(
tag!("html" tag!("body" 123).add_children(vec![1,2,3].into_iter().map(|c| tag!("div" c))))
.to_string()
.as_str(),
"<html><body>123<div>1</div><div>2</div><div>3</div></body></html>"
);
}
}