pochoir-parser 0.12.2

HTML parser for the pochoir template engine
Documentation
use cssparser::ToCss;
use pochoir_template_engine::TemplateBlock;
use precomputed_hash::PrecomputedHash;
use selectors::{
    matching,
    parser::{self, ParseRelative, SelectorList, SelectorParseErrorKind},
    Element, OpaqueElement,
};
use std::{
    fmt,
    hash::{DefaultHasher, Hash, Hasher},
};

use super::{TreeRef, TreeRefId};
use crate::Node;

/// Parse and build a [`SelectorList`] from an arbitrary string.
///
/// It is used in functions selecting elements like [`TreeRef::select`].
///
/// # Errors
///
/// Returns an error if the CSS selector is malformed.
///
/// [`SelectorList`]: selectors::parser::SelectorList
pub fn build_selector_list(
    selector: &str,
) -> Result<SelectorList<InnerSelector>, cssparser::ParseError<'_, SelectorParseErrorKind<'_>>> {
    let mut input = cssparser::ParserInput::new(selector);
    let mut parser = cssparser::Parser::new(&mut input);
    SelectorList::parse(&InnerSelectorParser, &mut parser, ParseRelative::No)
}

impl Element for TreeRef<'_, '_> {
    type Impl = InnerSelector;

    fn opaque(&self) -> OpaqueElement {
        OpaqueElement::new(&self.id)
    }

    fn parent_element(&self) -> Option<Self> {
        let parent = self.parent();

        if parent == *self {
            None
        } else {
            Some(parent)
        }
    }

    fn parent_node_is_shadow_root(&self) -> bool {
        false
    }

    fn containing_shadow_host(&self) -> Option<Self> {
        None
    }

    fn is_pseudo_element(&self) -> bool {
        false
    }

    fn prev_sibling_element(&self) -> Option<Self> {
        self.prev_sibling()
    }

    fn next_sibling_element(&self) -> Option<Self> {
        self.next_sibling()
    }

    fn first_element_child(&self) -> Option<Self> {
        self.children().next()
    }

    fn is_html_element_in_html_document(&self) -> bool {
        true
    }

    fn has_local_name(
        &self,
        local_name: &<Self::Impl as selectors::SelectorImpl>::BorrowedLocalName,
    ) -> bool {
        match self.data() {
            Node::Element(ref name, _) => *name == local_name.inner,
            _ => false,
        }
    }

    fn has_namespace(
        &self,
        _ns: &<Self::Impl as selectors::SelectorImpl>::BorrowedNamespaceUrl,
    ) -> bool {
        true
    }

    fn is_same_type(&self, other: &Self) -> bool {
        if let Node::Element(ref name, _) = self.data() {
            let name = name.clone();

            if let Node::Element(ref other_name, _) = other.data() {
                return name == *other_name;
            }
        }

        false
    }

    fn attr_matches(
        &self,
        _ns: &selectors::attr::NamespaceConstraint<
            &<Self::Impl as selectors::SelectorImpl>::NamespaceUrl,
        >,
        local_name: &<Self::Impl as selectors::SelectorImpl>::LocalName,
        operation: &selectors::attr::AttrSelectorOperation<
            &<Self::Impl as selectors::SelectorImpl>::AttrValue,
        >,
    ) -> bool {
        match self.data() {
            Node::Element(_, ref attrs) => attrs.iter().any(|(key, val)| {
                // Ignore template blocks
                *local_name == LocalName::from(&***key)
                    && operation.eval_str(
                        &val.iter()
                            .filter_map(|b| {
                                if let TemplateBlock::RawText(t) = &**b {
                                    Some(t.to_string())
                                } else {
                                    None
                                }
                            })
                            .collect::<String>(),
                    )
            }),
            _ => false,
        }
    }

    fn match_non_ts_pseudo_class(
        &self,
        _pc: &<Self::Impl as selectors::SelectorImpl>::NonTSPseudoClass,
        _context: &mut matching::MatchingContext<Self::Impl>,
    ) -> bool {
        false
    }

    fn match_pseudo_element(
        &self,
        _pe: &<Self::Impl as selectors::SelectorImpl>::PseudoElement,
        _context: &mut matching::MatchingContext<Self::Impl>,
    ) -> bool {
        false
    }

    fn apply_selector_flags(&self, _flags: matching::ElementSelectorFlags) {}

    fn is_link(&self) -> bool {
        matches!(self.data(), Node::Element(ref el_name, _) if el_name == "link")
    }

    fn is_html_slot_element(&self) -> bool {
        true
    }

    fn has_id(
        &self,
        id: &<Self::Impl as selectors::SelectorImpl>::Identifier,
        case_sensitivity: selectors::attr::CaseSensitivity,
    ) -> bool {
        if let Node::Element(_, _) = self.data() {
            self.attr("id")
                .unwrap_or_default()
                .is_some_and(|v| case_sensitivity.eq(v.as_bytes(), id.inner.as_bytes()))
        } else {
            false
        }
    }

    fn has_class(
        &self,
        name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
        case_sensitivity: selectors::attr::CaseSensitivity,
    ) -> bool {
        if let Node::Element(_, _) = self.data() {
            self.attr("class")
                .unwrap_or_default()
                .is_some_and(|v| case_sensitivity.eq(v.as_bytes(), name.inner.as_bytes()))
        } else {
            false
        }
    }

    fn imported_part(
        &self,
        _name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
    ) -> Option<<Self::Impl as selectors::SelectorImpl>::Identifier> {
        None
    }

    fn is_part(&self, _name: &<Self::Impl as selectors::SelectorImpl>::Identifier) -> bool {
        false
    }

    fn is_empty(&self) -> bool {
        !self
            .children()
            .any(|c| matches!(c.data(), Node::Element(_, _) | Node::TemplateBlock(_)))
    }

    fn is_root(&self) -> bool {
        self.id == TreeRefId::Root
    }

    fn assigned_slot(&self) -> Option<Self> {
        None
    }

    fn ignores_nth_child_selectors(&self) -> bool {
        false
    }

    fn has_custom_state(&self, _name: &<Self::Impl as parser::SelectorImpl>::Identifier) -> bool {
        false
    }

    fn add_element_unique_hashes(&self, _filter: &mut selectors::bloom::BloomFilter) -> bool {
        false
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LocalName {
    inner: String,
}

impl AsRef<str> for LocalName {
    fn as_ref(&self) -> &str {
        self.inner.as_str()
    }
}

impl From<&str> for LocalName {
    fn from(v: &str) -> Self {
        Self {
            inner: v.to_string(),
        }
    }
}

impl PrecomputedHash for LocalName {
    fn precomputed_hash(&self) -> u32 {
        let mut hasher = DefaultHasher::new();
        self.inner.hash(&mut hasher);
        hasher.finish() as u32
    }
}

impl ToCss for LocalName {
    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
        dest.write_str(&self.inner)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Namespace;

impl PrecomputedHash for Namespace {
    fn precomputed_hash(&self) -> u32 {
        0
    }
}

pub struct InnerSelectorParser;

impl<'i> parser::Parser<'i> for InnerSelectorParser {
    type Impl = InnerSelector;
    type Error = SelectorParseErrorKind<'i>;
}

#[derive(Debug, Clone)]
pub struct InnerSelector;

impl parser::SelectorImpl for InnerSelector {
    type ExtraMatchingData<'a> = String;
    type AttrValue = LocalName;
    type Identifier = LocalName;
    type LocalName = LocalName;
    type NamespaceUrl = Namespace;
    type NamespacePrefix = LocalName;
    type BorrowedLocalName = LocalName;
    type BorrowedNamespaceUrl = Namespace;

    type NonTSPseudoClass = NonTSPseudoClass;
    type PseudoElement = PseudoElement;
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct NonTSPseudoClass;

impl parser::NonTSPseudoClass for NonTSPseudoClass {
    type Impl = InnerSelector;

    fn is_active_or_hover(&self) -> bool {
        false
    }

    fn is_user_action_state(&self) -> bool {
        false
    }
}

impl ToCss for NonTSPseudoClass {
    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
        dest.write_str("")
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PseudoElement;

impl parser::PseudoElement for PseudoElement {
    type Impl = InnerSelector;
}

impl ToCss for PseudoElement {
    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
        dest.write_str("")
    }
}