use std::fmt;
use std::ops::Deref;
use std::collections::{HashMap, HashSet};
use std::collections::{hash_map, hash_set};
use html5ever::{Attribute, LocalName, QualName};
use html5ever::tendril::StrTendril;
use selectors::attr::CaseSensitivity;
#[derive(Clone, PartialEq, Eq)]
pub enum Node {
Document,
Fragment,
Doctype(Doctype),
Comment(Comment),
Text(Text),
Element(Element),
ProcessingInstruction(ProcessingInstruction),
}
impl Node {
pub fn is_document(&self) -> bool {
match *self {
Node::Document => true,
_ => false,
}
}
pub fn is_fragment(&self) -> bool {
match *self {
Node::Fragment => true,
_ => false,
}
}
pub fn is_doctype(&self) -> bool {
match *self {
Node::Doctype(_) => true,
_ => false,
}
}
pub fn is_comment(&self) -> bool {
match *self {
Node::Comment(_) => true,
_ => false,
}
}
pub fn is_text(&self) -> bool {
match *self {
Node::Text(_) => true,
_ => false,
}
}
pub fn is_element(&self) -> bool {
match *self {
Node::Element(_) => true,
_ => false,
}
}
pub fn as_doctype(&self) -> Option<&Doctype> {
match *self {
Node::Doctype(ref d) => Some(d),
_ => None,
}
}
pub fn as_comment(&self) -> Option<&Comment> {
match *self {
Node::Comment(ref c) => Some(c),
_ => None,
}
}
pub fn as_text(&self) -> Option<&Text> {
match *self {
Node::Text(ref t) => Some(t),
_ => None,
}
}
pub fn as_element(&self) -> Option<&Element> {
match *self {
Node::Element(ref e) => Some(e),
_ => None,
}
}
pub fn as_processing_instruction(&self) -> Option<&ProcessingInstruction> {
match *self {
Node::ProcessingInstruction(ref pi) => Some(pi),
_ => None,
}
}
}
impl fmt::Debug for Node {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Node::Document => write!(f, "Document"),
Node::Fragment => write!(f, "Fragment"),
Node::Doctype(ref d) => write!(f, "Doctype({:?})", d),
Node::Comment(ref c) => write!(f, "Comment({:?})", c),
Node::Text(ref t) => write!(f, "Text({:?})", t),
Node::Element(ref e) => write!(f, "Element({:?})", e),
Node::ProcessingInstruction(ref pi) => write!(f, "ProcessingInstruction({:?})", pi),
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Doctype {
pub name: StrTendril,
pub public_id: StrTendril,
pub system_id: StrTendril,
}
impl Doctype {
pub fn name(&self) -> &str {
self.name.deref()
}
pub fn public_id(&self) -> &str {
self.public_id.deref()
}
pub fn system_id(&self) -> &str {
self.system_id.deref()
}
}
impl fmt::Debug for Doctype {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
f,
"<!DOCTYPE {} PUBLIC {:?} {:?}>",
self.name(),
self.public_id(),
self.system_id()
)
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Comment {
pub comment: StrTendril,
}
impl Deref for Comment {
type Target = str;
fn deref(&self) -> &str {
self.comment.deref()
}
}
impl fmt::Debug for Comment {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "<!-- {:?} -->", self.deref())
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Text {
pub text: StrTendril,
}
impl Deref for Text {
type Target = str;
fn deref(&self) -> &str {
self.text.deref()
}
}
impl fmt::Debug for Text {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{:?}", self.deref())
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Element {
pub name: QualName,
pub id: Option<LocalName>,
pub classes: HashSet<LocalName>,
pub attrs: HashMap<QualName, StrTendril>,
}
impl Element {
#[doc(hidden)]
pub fn new(name: QualName, attrs: Vec<Attribute>) -> Self {
let id = attrs
.iter()
.find(|a| a.name.local.deref() == "id")
.map(|a| LocalName::from(a.value.deref()));
let classes: HashSet<LocalName> = attrs
.iter()
.find(|a| a.name.local.deref() == "class")
.map_or(HashSet::new(), |a| {
a.value
.deref()
.split_whitespace()
.map(LocalName::from)
.collect()
});
Element {
attrs: attrs.into_iter().map(|a| (a.name, a.value)).collect(),
name,
id,
classes,
}
}
pub fn name(&self) -> &str {
self.name.local.deref()
}
pub fn id(&self) -> Option<&str> {
self.id.as_ref().map(Deref::deref)
}
pub fn has_class(&self, class: &str, case_sensitive: CaseSensitivity) -> bool {
self.classes()
.any(|c| case_sensitive.eq(c.as_bytes(), class.as_bytes()))
}
pub fn classes(&self) -> Classes {
Classes {
inner: self.classes.iter(),
}
}
pub fn attr(&self, attr: &str) -> Option<&str> {
let qualname = QualName::new(None, ns!(), LocalName::from(attr));
self.attrs.get(&qualname).map(Deref::deref)
}
pub fn attrs(&self) -> Attrs {
Attrs {
inner: self.attrs.iter(),
}
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct Classes<'a> {
inner: hash_set::Iter<'a, LocalName>,
}
impl<'a> Iterator for Classes<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
self.inner.next().map(Deref::deref)
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct Attrs<'a> {
inner: hash_map::Iter<'a, QualName, StrTendril>,
}
impl<'a> Iterator for Attrs<'a> {
type Item = (&'a str, &'a str);
fn next(&mut self) -> Option<(&'a str, &'a str)> {
self.inner.next().map(|(k, v)| (k.local.deref(), v.deref()))
}
}
impl fmt::Debug for Element {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
try!(write!(f, "<{}", self.name()));
for (key, value) in self.attrs() {
try!(write!(f, " {}={:?}", key, value));
}
write!(f, ">")
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct ProcessingInstruction {
pub target: StrTendril,
pub data: StrTendril,
}
impl Deref for ProcessingInstruction {
type Target = str;
fn deref(&self) -> &str {
self.data.deref()
}
}
impl fmt::Debug for ProcessingInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{:?} {:?}", self, self.data)
}
}