use std::collections::{btree_map, BTreeMap};
use std::fmt::Debug;
use std::iter::FromIterator;
use std::ops::Index;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Token {
Char(char),
StartTag(StartTag),
EndTag(EndTag),
Comment(String),
Doctype(Doctype),
EndOfFile,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StartTag {
pub name: String,
pub self_closing: bool,
pub attributes: AttributeMap,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EndTag {
pub name: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Doctype {
pub force_quirks: bool,
pub name: Option<String>,
pub public_id: Option<String>,
pub system_id: Option<String>,
}
#[derive(Clone, Default, PartialEq, Eq)] pub struct AttributeMap {
pub(crate) inner: BTreeMap<String, AttrInternal>,
}
#[derive(Clone, Default, Eq)] pub(crate) struct AttrInternal {
pub value: String,
pub trace_idx: Option<AttributeTraceIdx>,
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct AttributeTraceIdx(
pub std::num::NonZeroUsize,
);
impl PartialEq for AttrInternal {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
#[derive(Eq, PartialEq)] pub struct Attribute<'a> {
name: &'a str,
map_val: &'a AttrInternal,
}
#[derive(Debug, PartialEq, Eq)]
pub struct AttributeOwned {
pub name: String,
pub value: String,
pub trace_idx: Option<AttributeTraceIdx>,
}
impl AttributeMap {
pub fn get(&self, name: &str) -> Option<&str> {
self.inner.get(name).map(|map_val| map_val.value.as_str())
}
pub fn value_and_trace_idx(&self, name: &str) -> Option<(&str, Option<AttributeTraceIdx>)> {
self.inner
.get(name)
.map(|map_val| (map_val.value.as_str(), map_val.trace_idx))
}
}
impl<'a> Attribute<'a> {
pub fn name(&self) -> &'a str {
self.name
}
pub fn value(&self) -> &'a str {
&self.map_val.value
}
pub fn trace_idx(&self) -> Option<AttributeTraceIdx> {
self.map_val.trace_idx
}
}
impl Index<&str> for AttributeMap {
type Output = str;
fn index(&self, name: &str) -> &Self::Output {
&self.inner[name].value
}
}
impl IntoIterator for AttributeMap {
type Item = AttributeOwned;
type IntoIter = AttrIntoIter;
fn into_iter(self) -> Self::IntoIter {
AttrIntoIter(self.inner.into_iter())
}
}
pub struct AttrIntoIter(btree_map::IntoIter<String, AttrInternal>);
impl Iterator for AttrIntoIter {
type Item = AttributeOwned;
fn next(&mut self) -> Option<Self::Item> {
let (name, map_val) = self.0.next()?;
Some(AttributeOwned {
name,
value: map_val.value,
trace_idx: map_val.trace_idx,
})
}
}
impl<'a> IntoIterator for &'a AttributeMap {
type Item = Attribute<'a>;
type IntoIter = AttrIter<'a>;
fn into_iter(self) -> Self::IntoIter {
AttrIter(self.inner.iter())
}
}
pub struct AttrIter<'a>(btree_map::Iter<'a, String, AttrInternal>);
impl<'a> Iterator for AttrIter<'a> {
type Item = Attribute<'a>;
fn next(&mut self) -> Option<Self::Item> {
let (name, map_val) = self.0.next()?;
Some(Attribute { name, map_val })
}
}
impl FromIterator<(String, String)> for AttributeMap {
fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
Self {
inner: iter
.into_iter()
.map(|(name, value)| {
(
name,
AttrInternal {
value,
trace_idx: None,
},
)
})
.collect(),
}
}
}
impl Debug for AttributeMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl Debug for AttrInternal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.value)?;
if let Some(idx) = self.trace_idx {
write!(f, " (trace #{})", idx.0)?;
}
Ok(())
}
}
impl Debug for Attribute<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Attribute")
.field("name", &self.name)
.field("value", &self.value())
.field("trace_idx", &self.trace_idx().map(|idx| idx.0))
.finish()
}
}