htmlite 0.13.0

An HTML manipulation toolkit
Documentation
#[doc(hidden)]
#[macro_export]
macro_rules! attributes_impl {
    ([$($acc:tt)*] $name:expr => $value:expr, $($rest:tt)*) => {
        $crate::attributes_impl!(
            [$($acc)* (String::from($name), String::from($value)),]
            $($rest)*
        )
    };

    ([$($acc:tt)*] $name:expr => $value:expr) => {
        $crate::attributes_impl!([$($acc)* (String::from($name), String::from($value)),])
    };

    ([$($acc:tt)*]) => {
        [$($acc)*]
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! html_impl {
    // (text "some text")
    (($arena:expr) [$($acc:tt)*] (text $text:expr) $($rest:tt)*) => {{
        let text = $arena.text($text);
        $crate::html_impl!(
            ($arena)
            [$($acc)* text,]
            $($rest)*
        )
    }};

    // (raw "some unescaped test")
    (($arena:expr) [$($acc:tt)*] (raw $text:expr) $($rest:tt)*) => {{
        let text = $arena.raw_text($text);
        $crate::html_impl!(
            ($arena)
            [$($acc)* text,]
            $($rest)*
        )
    }};

    // (>...)
    (($arena:expr) [$($acc:tt)*] (> $children:expr) $($rest:tt)*) => {{
        let fragment = $arena.fragment($children);
        $crate::html_impl!(
            ($arena)
            [$($acc)* fragment,]
            $($rest)*
        )
    }};


    // (["tag"] [<attributes>] ... )
    (($arena:expr) [$($acc:tt)*] ([$tagname:expr] [$($attributes:tt)*] $($children:tt)*) $($rest:tt)*) => {{
        let attributes = $crate::attributes_impl!([] $($attributes)*);
        let element = $arena.tag($tagname, attributes);
        element.append($crate::html_impl!(
            ($arena)
            []
            $($children)*
        ));
        $crate::html_impl!(
            ($arena)
            [$($acc)* element,]
            $($rest)*
        )
    }};

    // (tag [<attribtes>] ... )
    (($arena:expr) [$($acc:tt)*] ($tagname:ident [$($attributes:tt)*] $($children:tt)*) $($rest:tt)*) => {{
        let tagname = stringify!($tagname);
        $crate::html_impl!(
            ($arena)
            [$($acc)*]
            ([tagname] [$($attributes)*] $($children)*)
            $($rest)*
        )
    }};


    // (tag (...))
    (($arena:expr) [$($acc:tt)*] ($tagname:ident $($children:tt)*) $($rest:tt)*) => {{
        let tagname = stringify!($tagname);
        $crate::html_impl!(
            ($arena)
            [$($acc)*]
            ([tagname] [] $($children)*)
            $($rest)*
        )
    }};

    // (["tag"] (...))
    (($arena:expr) [$($acc:tt)*] ([$tagname:expr] $($children:tt)*) $($rest:tt)*) => {{
        let element = $arena.tag($tagname, []);
        $crate::html_impl!(
            ($arena)
            [$($acc)*]
            ([$tagname] [] $($children)*)
            $($rest)*
        )
    }};

    (($arena:expr) [$($top:tt)*]) => {
        $arena.fragment([$($top)*])
    }
}

/// This macro implements a syntax for creating HTML Nodes.
///
/// It accepts two arguments; a reference to a [`NodeArena`](crate::NodeArena) and some custom markup that represents your HTML.
///
/// # Markup syntax
///
/// The markup consistes of one or more elements, where each element is wrapped in parentheses.
/// The first identifier after the opening parentheses is the element's tag name.
///
/// ```
/// use htmlite::{NodeArena, html};
///
/// let arena = NodeArena::new();
/// let out = html!(
///     &arena,
///     (header)
///     (main)
///     (footer)
/// );
///
/// assert_eq!(out.html(), "<header></header><main></main><footer></footer>");
/// ```
///
/// Should you need to use a character that is not allowed as a rust identifier, you can wrap the name in brackets and use a string literal instead:
///
/// ```
/// use htmlite::{NodeArena, html};
///
/// let arena = NodeArena::new();
/// let out = html!(
///     &arena,
///     (["a weird name"])
///     (["!@wow"])
/// );
/// assert_eq!(out.html(), "<a weird name></a weird name><!@wow></!@wow>");
/// ```
///
/// Elements can have attributes.
/// These are expressed as a key-value list seperated by `=>`.
/// Both key & value must implement `Into<String>`.
///
/// ```
/// use htmlite::{NodeArena, html};
///
/// let arena = NodeArena::new();
/// let out = html!(
///     &arena,
///     (input ["readonly" => "", "placeholder" => "hello"])
/// );
///
/// assert_eq!(out.html(), r#"<input readonly="" placeholder="hello">"#)
/// ```
///
/// Elements can have children.
/// These come after any attributes
///
/// ```
/// use htmlite::{NodeArena, html};
///
/// let arena = NodeArena::new();
/// let out = html!(
///     &arena,
///     (section
///         ["class" => "container"]
///         (div)
///         (p (span))
///     )
/// );
/// assert_eq!(out.html(), r#"<section class="container"><div></div><p><span></span></p></section>"#)
/// ```
///
/// Use `text` for HTML-escaped text, and `raw` for unescaped text:
///
/// ```
/// use htmlite::{NodeArena, html};
///
/// let arena = NodeArena::new();
/// let out = html!(
///     &arena,
///     (p
///         (span (text "<a>"))
///         (span (raw "<a>"))
///     )
/// );
/// assert_eq!(out.html(), "<p><span>&lt;a&gt;</span><span><a></span></p>")
/// ```
///
/// Lastly, use `>` to interpolate any rust expression that returns a [`Node`](crate::Node) or iterator of `Node`s.
///
/// ```
/// use htmlite::{NodeArena, html};
///
/// let arena = NodeArena::new();
///
/// let snippet = "<div><p><li>One</li><li>Two</li></p></div>";
///
/// let parsed = htmlite::parse(&arena, snippet).unwrap();
///
/// let dst = html!(
///     &arena,
///     (ul (> parsed.descendants().select("li")))
/// );
///
/// assert_eq!(dst.html(), "<ul><li>One</li><li>Two</li></ul>");
///
/// ```
#[macro_export]
macro_rules! html {
    ($arena:expr) => {
        $arena.fragment([])
    };

    ($arena:expr, $($dsl:tt)*) => {
        $crate::html_impl!(($arena) [] $($dsl)*)
    };
}