use std::collections::HashMap; use std::fmt::{Debug, Formatter}; use std::str::from_utf8; use crate::common::spec::tag::ns::Namespace; pub mod c14n; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum ElementClosingTag { Omitted, Present, SelfClosing, Void, } #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum ScriptOrStyleLang { CSS, Data, JS, } pub struct AttrVal { // For serialisation only, not used for equality or value. pub quote: Option, pub value: Vec, } impl AttrVal { pub fn as_slice(&self) -> &[u8] { self.value.as_slice() } } impl Debug for AttrVal { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(from_utf8(&self.value).unwrap()) } } impl PartialEq for AttrVal { fn eq(&self, other: &Self) -> bool { self.value == other.value } } impl Eq for AttrVal {} // Derive Eq for testing. #[derive(Eq, PartialEq)] pub enum NodeData { Bang { code: Vec, // If the source unexpectedly ended before `>`, we can't add it, as otherwise output could be longer than source. ended: bool, }, Comment { code: Vec, // If the source unexpectedly ended before `-->`, we can't add it, as otherwise output could be longer than source. ended: bool, }, Doctype { legacy: Vec, // If the source unexpectedly ended before `>`, we can't add it, as otherwise output could be longer than source. ended: bool, }, Element { attributes: HashMap, AttrVal>, children: Vec, // If the source doesn't have a closing tag, then we can't add one, as otherwise output could be longer than source. closing_tag: ElementClosingTag, name: Vec, namespace: Namespace, // WARNING: This should only be set during minification, as minification can alter tree (e.g. remove text nodes completely). // If the next text or element sibling is an element, this will be set to its tag name. // Otherwise, this will be empty. It should be empty on creation. next_sibling_element_name: Vec, }, Instruction { code: Vec, // If the source unexpectedly ended before `?>`, we can't add it, as otherwise output could be longer than source. ended: bool, }, // Entities should not be decoded in ScriptOrStyleContent. ScriptOrStyleContent { code: Vec, lang: ScriptOrStyleLang, }, Text { value: Vec, }, } impl Debug for NodeData { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { NodeData::Bang { code, ended } => f .debug_struct("Bang") .field("code", &from_utf8(code).unwrap().to_string()) .field("ended", ended) .finish(), NodeData::Comment { code, ended } => f .debug_struct("Comment") .field("code", &from_utf8(code).unwrap().to_string()) .field("ended", ended) .finish(), NodeData::Doctype { legacy, ended } => f .debug_struct("Doctype") .field("legacy", &from_utf8(legacy).unwrap().to_string()) .field("ended", ended) .finish(), NodeData::Element { attributes, children, closing_tag, name, namespace, next_sibling_element_name, } => f .debug_struct("Element") .field("tag", &{ let mut out = format!("{:?}:{}", namespace, from_utf8(name).unwrap()); for (n, v) in attributes { out.push_str(format!(" {}={:?}", from_utf8(n).unwrap(), v).as_str()); } out }) .field("children", children) .field("closing_tag", closing_tag) .field( "next_sibling_element_name", &from_utf8(next_sibling_element_name).unwrap().to_string(), ) .finish(), NodeData::Instruction { code, ended } => f .debug_struct("Instruction") .field("code", &from_utf8(code).unwrap().to_string()) .field("ended", ended) .finish(), NodeData::ScriptOrStyleContent { code, lang } => f .debug_struct("ScriptOrStyleContent") .field("code", &from_utf8(code).unwrap().to_string()) .field("lang", lang) .finish(), NodeData::Text { value } => f.write_str(from_utf8(value).unwrap()), } } }