use crate::rewritable_units::{Comment, Doctype, DocumentEnd, Element, EndTag, TextChunk};
use crate::selectors_vm::Selector;
use super::AsciiCompatibleEncoding;
use std::borrow::Cow;
use std::error::Error;
#[diagnostic::on_unimplemented(
note = "If `{Self}` is a generic type, add `{Self}: HandlerTypes` trait bound, otherwise replace `{Self}` with `LocalHandlerTypes`",
note = "The concrete type of `{Self}` can only be either `LocalHandlerTypes` to allow non-`Send` closures in content handlers, or `SendHandlerTypes` to require `Send` closures"
)]
pub trait HandlerTypes: Sized {
type DoctypeHandler<'handler>: FnMut(&mut Doctype<'_>) -> HandlerResult + 'handler;
type CommentHandler<'handler>: FnMut(&mut Comment<'_>) -> HandlerResult + 'handler;
type TextHandler<'handler>: FnMut(&mut TextChunk<'_>) -> HandlerResult + 'handler;
type ElementHandler<'handler>: FnMut(&mut Element<'_, '_, Self>) -> HandlerResult + 'handler;
type EndTagHandler<'handler>: FnOnce(&mut EndTag<'_>) -> HandlerResult + 'handler;
type EndHandler<'handler>: FnOnce(&mut DocumentEnd<'_>) -> HandlerResult + 'handler;
#[doc(hidden)]
fn new_end_tag_handler<'handler>(
handler: impl IntoHandler<EndTagHandlerSend<'handler>>,
) -> Self::EndTagHandler<'handler>;
#[doc(hidden)]
fn new_element_handler<'handler>(
handler: impl IntoHandler<ElementHandlerSend<'handler, Self>>,
) -> Self::ElementHandler<'handler>;
#[doc(hidden)]
fn combine_handlers(handlers: Vec<Self::EndTagHandler<'_>>) -> Self::EndTagHandler<'_>;
}
pub struct LocalHandlerTypes {}
impl HandlerTypes for LocalHandlerTypes {
type DoctypeHandler<'h> = DoctypeHandler<'h>;
type CommentHandler<'h> = CommentHandler<'h>;
type TextHandler<'h> = TextHandler<'h>;
type ElementHandler<'h> = ElementHandler<'h>;
type EndTagHandler<'h> = EndTagHandler<'h>;
type EndHandler<'h> = EndHandler<'h>;
fn new_end_tag_handler<'h>(
handler: impl IntoHandler<EndTagHandlerSend<'h>>,
) -> Self::EndTagHandler<'h> {
handler.into_handler()
}
fn new_element_handler<'h>(
handler: impl IntoHandler<ElementHandlerSend<'h, Self>>,
) -> Self::ElementHandler<'h> {
handler.into_handler()
}
fn combine_handlers(handlers: Vec<Self::EndTagHandler<'_>>) -> Self::EndTagHandler<'_> {
Box::new(move |end_tag: &mut EndTag<'_>| {
for handler in handlers {
handler(end_tag)?;
}
Ok(())
})
}
}
#[doc(hidden)]
pub struct SendHandlerTypes {}
impl HandlerTypes for SendHandlerTypes {
type DoctypeHandler<'h> = DoctypeHandlerSend<'h>;
type CommentHandler<'h> = CommentHandlerSend<'h>;
type TextHandler<'h> = TextHandlerSend<'h>;
type ElementHandler<'h> = ElementHandlerSend<'h, Self>;
type EndTagHandler<'h> = EndTagHandlerSend<'h>;
type EndHandler<'h> = EndHandlerSend<'h>;
fn new_end_tag_handler<'h>(
handler: impl IntoHandler<Self::EndTagHandler<'h>>,
) -> Self::EndTagHandler<'h> {
handler.into_handler()
}
fn new_element_handler<'h>(
handler: impl IntoHandler<Self::ElementHandler<'h>>,
) -> Self::ElementHandler<'h> {
handler.into_handler()
}
fn combine_handlers(handlers: Vec<Self::EndTagHandler<'_>>) -> Self::EndTagHandler<'_> {
Box::new(move |end_tag: &mut EndTag<'_>| {
for handler in handlers {
handler(end_tag)?;
}
Ok(())
})
}
}
pub type HandlerResult = Result<(), Box<dyn Error + Send + Sync + 'static>>;
pub type DoctypeHandler<'h> = Box<dyn FnMut(&mut Doctype<'_>) -> HandlerResult + 'h>;
pub type CommentHandler<'h> = Box<dyn FnMut(&mut Comment<'_>) -> HandlerResult + 'h>;
pub type TextHandler<'h> = Box<dyn FnMut(&mut TextChunk<'_>) -> HandlerResult + 'h>;
pub type ElementHandler<'h, H = LocalHandlerTypes> =
Box<dyn FnMut(&mut Element<'_, '_, H>) -> HandlerResult + 'h>;
pub type EndTagHandler<'h> = Box<dyn FnOnce(&mut EndTag<'_>) -> HandlerResult + 'h>;
pub type EndHandler<'h> = Box<dyn FnOnce(&mut DocumentEnd<'_>) -> HandlerResult + 'h>;
pub type DoctypeHandlerSend<'h> = Box<dyn FnMut(&mut Doctype<'_>) -> HandlerResult + Send + 'h>;
pub type CommentHandlerSend<'h> = Box<dyn FnMut(&mut Comment<'_>) -> HandlerResult + Send + 'h>;
pub type TextHandlerSend<'h> = Box<dyn FnMut(&mut TextChunk<'_>) -> HandlerResult + Send + 'h>;
pub type ElementHandlerSend<'h, H = SendHandlerTypes> =
Box<dyn FnMut(&mut Element<'_, '_, H>) -> HandlerResult + Send + 'h>;
pub type EndTagHandlerSend<'h> = Box<dyn FnOnce(&mut EndTag<'_>) -> HandlerResult + Send + 'h>;
pub type EndHandlerSend<'h> = Box<dyn FnOnce(&mut DocumentEnd<'_>) -> HandlerResult + Send + 'h>;
#[diagnostic::on_unimplemented(
message = "Handler could not be made from `{Self}`\nThe internal `IntoHandler` trait is implemented for closures like `FnMut(&mut _) -> HandlerResult` and `FnOnce(&mut _) -> HandlerResult`, with `+ Send` if needed",
note = "Ensure that the closure's arguments are correct (add explicit parameter types if needed) and that it implements `Send` if using `Send`-able handlers"
)]
#[doc(hidden)]
pub trait IntoHandler<T: Sized> {
fn into_handler(self) -> T;
}
impl<'h, F: FnMut(&mut Doctype<'_>) -> HandlerResult + 'h> IntoHandler<DoctypeHandler<'h>> for F {
fn into_handler(self) -> DoctypeHandler<'h> {
Box::new(self)
}
}
impl<'h, F: FnMut(&mut Comment<'_>) -> HandlerResult + 'h> IntoHandler<CommentHandler<'h>> for F {
fn into_handler(self) -> CommentHandler<'h> {
Box::new(self)
}
}
impl<'h, F: FnMut(&mut TextChunk<'_>) -> HandlerResult + 'h> IntoHandler<TextHandler<'h>> for F {
fn into_handler(self) -> TextHandler<'h> {
Box::new(self)
}
}
impl<'h, F: FnMut(&mut Element<'_, '_, LocalHandlerTypes>) -> HandlerResult + 'h>
IntoHandler<ElementHandler<'h>> for F
{
fn into_handler(self) -> ElementHandler<'h> {
Box::new(self)
}
}
impl<'h, F: FnOnce(&mut EndTag<'_>) -> HandlerResult + 'h> IntoHandler<EndTagHandler<'h>> for F {
fn into_handler(self) -> EndTagHandler<'h> {
Box::new(self)
}
}
impl<'h, F: FnOnce(&mut DocumentEnd<'_>) -> HandlerResult + 'h> IntoHandler<EndHandler<'h>> for F {
fn into_handler(self) -> EndHandler<'h> {
Box::new(self)
}
}
impl<'h, F: FnMut(&mut Doctype<'_>) -> HandlerResult + Send + 'h>
IntoHandler<DoctypeHandlerSend<'h>> for F
{
fn into_handler(self) -> DoctypeHandlerSend<'h> {
Box::new(self)
}
}
impl<'h, F: FnMut(&mut Comment<'_>) -> HandlerResult + Send + 'h>
IntoHandler<CommentHandlerSend<'h>> for F
{
fn into_handler(self) -> CommentHandlerSend<'h> {
Box::new(self)
}
}
impl<'h, F: FnMut(&mut TextChunk<'_>) -> HandlerResult + Send + 'h> IntoHandler<TextHandlerSend<'h>>
for F
{
fn into_handler(self) -> TextHandlerSend<'h> {
Box::new(self)
}
}
impl<'h, H: HandlerTypes, F: FnMut(&mut Element<'_, '_, H>) -> HandlerResult + Send + 'h>
IntoHandler<ElementHandlerSend<'h, H>> for F
{
fn into_handler(self) -> ElementHandlerSend<'h, H> {
Box::new(self)
}
}
impl<'h, F: FnOnce(&mut EndTag<'_>) -> HandlerResult + Send + 'h> IntoHandler<EndTagHandlerSend<'h>>
for F
{
fn into_handler(self) -> EndTagHandlerSend<'h> {
Box::new(self)
}
}
impl<'h, F: FnOnce(&mut DocumentEnd<'_>) -> HandlerResult + Send + 'h>
IntoHandler<EndHandlerSend<'h>> for F
{
fn into_handler(self) -> EndHandlerSend<'h> {
Box::new(self)
}
}
pub struct ElementContentHandlers<'h, H: HandlerTypes = LocalHandlerTypes> {
pub element: Option<H::ElementHandler<'h>>,
pub comments: Option<H::CommentHandler<'h>>,
pub text: Option<H::TextHandler<'h>>,
}
impl<H: HandlerTypes> Default for ElementContentHandlers<'_, H> {
fn default() -> Self {
ElementContentHandlers {
element: None,
comments: None,
text: None,
}
}
}
impl<'h, H: HandlerTypes> ElementContentHandlers<'h, H> {
#[inline]
#[must_use]
pub fn element(mut self, handler: impl IntoHandler<H::ElementHandler<'h>>) -> Self {
self.element = Some(handler.into_handler());
self
}
#[inline]
#[must_use]
pub fn comments(mut self, handler: impl IntoHandler<H::CommentHandler<'h>>) -> Self {
self.comments = Some(handler.into_handler());
self
}
#[inline]
#[must_use]
pub fn text(mut self, handler: impl IntoHandler<H::TextHandler<'h>>) -> Self {
self.text = Some(handler.into_handler());
self
}
}
pub struct DocumentContentHandlers<'h, H: HandlerTypes = LocalHandlerTypes> {
pub doctype: Option<H::DoctypeHandler<'h>>,
pub comments: Option<H::CommentHandler<'h>>,
pub text: Option<H::TextHandler<'h>>,
pub end: Option<H::EndHandler<'h>>,
}
impl<H: HandlerTypes> Default for DocumentContentHandlers<'_, H> {
fn default() -> Self {
DocumentContentHandlers {
doctype: None,
comments: None,
text: None,
end: None,
}
}
}
impl<'h, H: HandlerTypes> DocumentContentHandlers<'h, H> {
#[inline]
#[must_use]
pub fn doctype(mut self, handler: impl IntoHandler<H::DoctypeHandler<'h>>) -> Self {
self.doctype = Some(handler.into_handler());
self
}
#[inline]
#[must_use]
pub fn comments(mut self, handler: impl IntoHandler<H::CommentHandler<'h>>) -> Self {
self.comments = Some(handler.into_handler());
self
}
#[inline]
#[must_use]
pub fn text(mut self, handler: impl IntoHandler<H::TextHandler<'h>>) -> Self {
self.text = Some(handler.into_handler());
self
}
#[inline]
#[must_use]
pub fn end(mut self, handler: impl IntoHandler<H::EndHandler<'h>>) -> Self {
self.end = Some(handler.into_handler());
self
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __element_content_handler {
($selector:expr, $handler_name:ident, $handler:expr) => {
(
::std::borrow::Cow::Owned($selector.parse::<$crate::Selector>().unwrap()),
$crate::ElementContentHandlers::default().$handler_name($handler),
)
};
}
#[macro_export(local_inner_macros)]
macro_rules! element {
($selector:expr, $handler:expr) => {{
#[inline(always)]
const fn type_hint<'h, T, H: $crate::HandlerTypes>(h: T) -> T
where
T: FnMut(&mut $crate::html_content::Element<'_, '_, H>) -> $crate::HandlerResult + 'h,
{
h
}
__element_content_handler!($selector, element, type_hint($handler))
}};
}
#[macro_export(local_inner_macros)]
macro_rules! text {
($selector:expr, $handler:expr) => {{
#[inline(always)]
fn type_hint<T>(h: T) -> T
where
T: FnMut(&mut $crate::html_content::TextChunk) -> $crate::HandlerResult,
{
h
}
__element_content_handler!($selector, text, type_hint($handler))
}};
}
#[macro_export(local_inner_macros)]
macro_rules! comments {
($selector:expr, $handler:expr) => {{
#[inline(always)]
const fn type_hint<T>(h: T) -> T
where
T: FnMut(&mut $crate::html_content::Comment<'_>) -> $crate::HandlerResult,
{
h
}
__element_content_handler!($selector, comments, type_hint($handler))
}};
}
#[macro_export(local_inner_macros)]
macro_rules! streaming {
($closure:expr) => {{
use ::std::error::Error;
use $crate::html_content::StreamingHandlerSink;
#[inline(always)]
const fn streaming_macro_type_hint<StreamingHandler>(
handler_closure: StreamingHandler,
) -> StreamingHandler
where
StreamingHandler:
FnOnce(&mut StreamingHandlerSink<'_>) -> Result<(), Box<dyn Error + Send + Sync>> + 'static,
{
handler_closure
}
Box::new(streaming_macro_type_hint($closure))
as Box<dyn $crate::html_content::StreamingHandler + Send>
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __document_content_handler {
($handler_name:ident, $handler:expr) => {
$crate::DocumentContentHandlers::default().$handler_name($handler)
};
}
#[macro_export(local_inner_macros)]
macro_rules! doctype {
($handler:expr) => {{
#[inline(always)]
const fn type_hint<T>(h: T) -> T
where
T: FnMut(&mut $crate::html_content::Doctype<'_>) -> $crate::HandlerResult,
{
h
}
__document_content_handler!(doctype, type_hint($handler))
}};
}
#[macro_export(local_inner_macros)]
macro_rules! doc_text {
($handler:expr) => {{
#[inline(always)]
const fn type_hint<T>(h: T) -> T
where
T: FnMut(&mut $crate::html_content::TextChunk<'_>) -> $crate::HandlerResult,
{
h
}
__document_content_handler!(text, type_hint($handler))
}};
}
#[macro_export(local_inner_macros)]
macro_rules! doc_comments {
($handler:expr) => {{
#[inline(always)]
const fn type_hint<T>(h: T) -> T
where
T: FnMut(&mut $crate::html_content::Comment<'_>) -> $crate::HandlerResult,
{
h
}
__document_content_handler!(comments, type_hint($handler))
}};
}
#[macro_export(local_inner_macros)]
macro_rules! end {
($handler:expr) => {{
#[inline(always)]
const fn type_hint<T>(h: T) -> T
where
T: FnOnce(&mut $crate::html_content::DocumentEnd<'_>) -> $crate::HandlerResult,
{
h
}
__document_content_handler!(end, type_hint($handler))
}};
}
#[repr(C)]
pub struct MemorySettings {
pub preallocated_parsing_buffer_size: usize,
pub max_allowed_memory_usage: usize,
}
impl Default for MemorySettings {
#[inline]
fn default() -> Self {
Self {
preallocated_parsing_buffer_size: 1024,
max_allowed_memory_usage: usize::MAX,
}
}
}
impl MemorySettings {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
pub struct Settings<'handlers, 'selectors, H: HandlerTypes = LocalHandlerTypes> {
pub element_content_handlers: Vec<(
Cow<'selectors, Selector>,
ElementContentHandlers<'handlers, H>,
)>,
pub document_content_handlers: Vec<DocumentContentHandlers<'handlers, H>>,
pub encoding: AsciiCompatibleEncoding,
pub memory_settings: MemorySettings,
pub strict: bool,
pub enable_esi_tags: bool,
pub adjust_charset_on_meta_tag: bool,
}
impl Default for Settings<'_, '_, LocalHandlerTypes> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Settings<'_, '_, LocalHandlerTypes> {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::new_for_handler_types()
}
}
impl Settings<'_, '_, SendHandlerTypes> {
#[inline]
#[must_use]
pub fn new_send() -> Self {
Self::new_for_handler_types()
}
}
impl<H: HandlerTypes> Settings<'_, '_, H> {
#[inline]
#[must_use]
pub fn new_for_handler_types() -> Self {
Settings {
element_content_handlers: vec![],
document_content_handlers: vec![],
encoding: AsciiCompatibleEncoding(encoding_rs::UTF_8),
memory_settings: MemorySettings::default(),
strict: true,
enable_esi_tags: false,
adjust_charset_on_meta_tag: false,
}
}
}
impl<'h, 's, H: HandlerTypes> From<RewriteStrSettings<'h, 's, H>> for Settings<'h, 's, H> {
#[inline]
fn from(settings: RewriteStrSettings<'h, 's, H>) -> Self {
Settings {
element_content_handlers: settings.element_content_handlers,
document_content_handlers: settings.document_content_handlers,
strict: settings.strict,
enable_esi_tags: settings.enable_esi_tags,
..Settings::new_for_handler_types()
}
}
}
pub struct RewriteStrSettings<'handlers, 'selectors, H: HandlerTypes = LocalHandlerTypes> {
pub element_content_handlers: Vec<(
Cow<'selectors, Selector>,
ElementContentHandlers<'handlers, H>,
)>,
pub document_content_handlers: Vec<DocumentContentHandlers<'handlers, H>>,
pub strict: bool,
pub enable_esi_tags: bool,
}
impl Default for RewriteStrSettings<'_, '_, LocalHandlerTypes> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl RewriteStrSettings<'_, '_, LocalHandlerTypes> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self::new_for_handler_types()
}
}
impl RewriteStrSettings<'_, '_, SendHandlerTypes> {
#[inline]
#[must_use]
pub const fn new_send() -> Self {
Self::new_for_handler_types()
}
}
impl<H: HandlerTypes> RewriteStrSettings<'_, '_, H> {
#[inline]
#[must_use]
pub const fn new_for_handler_types() -> Self {
RewriteStrSettings {
element_content_handlers: vec![],
document_content_handlers: vec![],
strict: true,
enable_esi_tags: true,
}
}
}