use pochoir_common::Spanned;
use pochoir_template_engine::TemplateBlock;
use std::{borrow::Cow, slice};
pub type AttrKey<'a> = Cow<'a, str>;
pub type AttrVal<'a> = Vec<Spanned<TemplateBlock<'a>>>;
pub type Attr<'a> = (Spanned<AttrKey<'a>>, Spanned<AttrVal<'a>>);
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Attrs<'a>(Vec<Attr<'a>>);
impl<'a> Attrs<'a> {
pub fn new() -> Self {
Self(vec![])
}
pub fn contains_key<'b, K: Into<AttrKey<'b>>>(&self, key: K) -> bool {
let key = key.into();
self.0.iter().any(|a| *a.0 == key)
}
pub fn get<'b, K: Into<AttrKey<'b>>>(&self, key: K) -> Option<&AttrVal<'a>> {
let key = key.into();
self.0.iter().find(|a| *a.0 == key).map(|a| &*a.1)
}
pub fn get_key_value<'b, K: Into<AttrKey<'b>>>(&self, key: K) -> Option<&Attr<'a>> {
let key = key.into();
self.0.iter().find(|a| *a.0 == key)
}
pub fn get_mut<'b, K: Into<AttrKey<'b>>>(&mut self, key: K) -> Option<&mut AttrVal<'a>> {
let key = key.into();
self.0.iter_mut().find(|a| *a.0 == key).map(|a| &mut *a.1)
}
pub fn insert<K: Into<AttrKey<'a>>, V: Into<AttrVal<'a>>>(&mut self, key: K, val: V) {
let key = key.into();
let val = val.into();
if let Some(attribute) = self.0.iter_mut().find(|a| *a.0 == key) {
attribute.0 = Spanned::new(key);
attribute.1 = Spanned::new(val);
} else {
self.0.push((Spanned::new(key), Spanned::new(val)));
}
}
pub fn insert_spanned<K: Into<AttrKey<'a>>, V: Into<AttrVal<'a>>>(
&mut self,
key: Spanned<K>,
val: Spanned<V>,
) {
let key = key.map_spanned(Into::into);
let val = val.map_spanned(Into::into);
if let Some(attribute) = self.0.iter_mut().find(|a| a.0 == key) {
attribute.0 = key;
attribute.1 = val;
} else {
self.0.push((key, val));
}
}
pub fn remove<'b, K: Into<AttrKey<'b>>>(&mut self, key: K) -> Option<Attr<'a>> {
let key = key.into();
if let Some(index) = self.0.iter().enumerate().find(|(_, a)| *a.0 == key) {
Some(self.0.remove(index.0))
} else {
None
}
}
#[inline]
pub fn iter(&self) -> slice::Iter<'_, Attr<'a>> {
self.0.iter()
}
#[inline]
pub fn iter_mut(&'a mut self) -> slice::IterMut<'a, Attr<'a>> {
self.0.iter_mut()
}
pub fn deep_clone<'b>(self) -> Attrs<'b> {
self.0
.into_iter()
.map(|a| {
(
a.0.map_spanned(|k| Cow::Owned(k.into_owned())),
a.1.map_spanned(|v| {
v.into_iter()
.map(|v| v.map_spanned(TemplateBlock::deep_clone))
.collect()
}),
)
})
.collect()
}
}
impl<'a> FromIterator<Attr<'a>> for Attrs<'a> {
fn from_iter<T: IntoIterator<Item = Attr<'a>>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
impl<'a> FromIterator<(AttrKey<'a>, AttrVal<'a>)> for Attrs<'a> {
fn from_iter<T: IntoIterator<Item = (AttrKey<'a>, AttrVal<'a>)>>(iter: T) -> Self {
Self(
iter.into_iter()
.map(|(k, v)| (Spanned::new(k), Spanned::new(v)))
.collect(),
)
}
}
impl<'a, 'b> IntoIterator for &'a Attrs<'b> {
type Item = &'a Attr<'b>;
type IntoIter = slice::Iter<'a, Attr<'b>>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a, 'b> IntoIterator for &'a mut Attrs<'b> {
type Item = &'a mut Attr<'b>;
type IntoIter = slice::IterMut<'a, Attr<'b>>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
impl<'a> From<Vec<Attr<'a>>> for Attrs<'a> {
fn from(v: Vec<Attr<'a>>) -> Self {
Self(v)
}
}
impl<'a> From<&[Attr<'a>]> for Attrs<'a> {
fn from(v: &[Attr<'a>]) -> Self {
Self(v.to_vec())
}
}
impl<'a, const N: usize> From<[Attr<'a>; N]> for Attrs<'a> {
fn from(v: [Attr<'a>; N]) -> Self {
Self(v.to_vec())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Node<'a> {
Element(Cow<'a, str>, Attrs<'a>),
Comment(Cow<'a, str>),
Doctype(Cow<'a, str>),
TemplateBlock(TemplateBlock<'a>),
Root,
}
impl<'a> Node<'a> {
pub(crate) fn node_type(&self) -> &'static str {
match self {
Node::Element(_, _) => "Element",
Node::Comment(_) => "Comment",
Node::Doctype(_) => "Doctype",
Node::TemplateBlock(_) => "TemplateBlock",
Node::Root => "Root",
}
}
pub fn new_simple_element<
S1: Into<Cow<'a, str>>,
S2: Into<Cow<'a, str>>,
S3: Into<Cow<'a, str>>,
I: IntoIterator<Item = (S2, S3)>,
>(
element_name: S1,
attrs: I,
) -> Self {
Self::Element(
element_name.into(),
attrs
.into_iter()
.map(|(k, v)| {
(
k.into(),
vec![Spanned::new(TemplateBlock::RawText(v.into()))],
)
})
.collect::<Attrs>(),
)
}
pub fn new_element_without_attrs<S: Into<Cow<'a, str>>>(element_name: S) -> Self {
Self::Element(element_name.into(), Attrs::new())
}
pub fn new_comment<S: Into<Cow<'a, str>>>(comment_value: S) -> Self {
Self::Comment(comment_value.into())
}
pub fn new_doctype<S: Into<Cow<'a, str>>>(doctype_value: S) -> Self {
Self::Doctype(doctype_value.into())
}
}
impl Node<'_> {
pub fn deep_clone<'b>(self) -> Node<'b> {
match self {
Node::Element(a, b) => Node::Element(Cow::Owned(a.into_owned()), b.deep_clone()),
Node::Comment(a) => Node::Comment(Cow::Owned(a.into_owned())),
Node::Doctype(a) => Node::Doctype(Cow::Owned(a.into_owned())),
Node::TemplateBlock(a) => Node::TemplateBlock(a.deep_clone()),
Node::Root => Node::Root,
}
}
}
pub type ParsedNode<'a> = Spanned<Node<'a>>;