1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
//! Typed HTML nodes.
//!
//! # Examples
//!
//! ```rust
//! use html_node::typed::{self, elements::*};
//! // ^^^^^^^^^^^
//! // required to bring type definitions
//! // of all basic html elements into
//! // the current scope.
//! // (can also use `elements::div`, etc.)
//!
//! #[derive(Clone, Debug)]
//! struct Location {
//! x: i32,
//! y: i32,
//! }
//!
//! impl std::fmt::Display for Location {
//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//! write!(f, "{},{}", self.x, self.y)
//! }
//! }
//!
//! // defines a custom element named `CustomElement`, with the specified attributes.
//! // underscores in attributes get converted to and from hyphens in the
//! // `typed::html!` macro and rendering.
//!
//! // note that global attributes like `id` will be pre-defined when
//! // using the `typed::element!` macro.
//!
//! typed::element! {
//! CustomElement("custom-element") {
//! custom_attr, // implictly typed as a `String`
//! location: Location,
//! }
//! }
//!
//! typed::attributes! {
//! [TestAttrs] {
//! test_val: i32,
//! }
//! }
//!
//! // creates a normal `Node`, but checks types at compile-time!
//! let html = typed::html! { (test: TestAttrs, any)
//! // ^^^^^^^^^^^^^^^^^^^^^^ these are extension attributes.
//! // they are not required, but allow you to specify extra attributes
//! // which will be available within this macro invocation.
//! // those of the form `attr-prefix: Type` will be type checked, and
//! // those with just `attr-prefix` will be considered "catch-all" prefixes
//! // allowing any attribute with that prefix to be specified.
//! // `data-*` and `aria-*` are predefined as catch-all prefixes.
//! <div id="container">
//! <CustomElement test-val=42 any-whatever data-cool=true id="el" custom-attr="test" location=Location { x: 1, y: 2 } />
//! </div>
//! };
//!
//! assert_eq!(
//! html.to_string(),
//! "\
//! <div id=\"container\">\
//! <custom-element id=\"el\" custom-attr=\"test\" location=\"1,2\" test-val=\"42\" any-whatever data-cool=\"true\">\
//! </custom-element>\
//! </div>\
//! ",
//! );
#[allow(clippy::module_name_repetitions)]
pub use html_node_core::typed::{elements, TypedAttributes, TypedElement};
/// Make a typed set of HTML attributes.
///
/// Used internally by [`element!`].
pub use html_node_core::typed_attributes as attributes;
/// Make a typed element.
///
/// # Examples
///
/// ## Fully Generated (With Custom Name)
///
/// ```rust
/// use html_node::typed;
///
/// typed::element! {
/// CustomElement("custom-element") {
/// custom_attr,
/// }
/// }
///
/// // note that global attributes like `id` will be pre-defined when
/// // using the `typed::element!` macro.
/// assert_eq!(
/// typed::html!(<CustomElement id="el" custom-attr="test" />).to_string(),
/// r#"<custom-element id="el" custom-attr="test"></custom-element>"#,
/// );
/// ```
///
/// ## Fully Generated (With Default Name)
///
/// ```rust
/// use html_node::typed;
///
/// typed::element! {
/// CustomElement {
/// custom_attr,
/// }
/// }
///
/// assert_eq!(
/// typed::html!(<CustomElement id="el" custom-attr="test" />).to_string(),
/// r#"<CustomElement id="el" custom-attr="test"></CustomElement>"#,
/// );
/// ```
///
/// ## Generated With Custom Attributes Name
///
/// ```rust
/// use html_node::typed::{self, TypedAttributes};
///
/// typed::element! {
/// CustomElement [CustomElementAttributesDifferent] {
/// custom_attr,
/// }
/// }
///
/// assert_eq!(
/// typed::html!(<CustomElement id="el" custom-attr="test" />).to_string(),
/// r#"<CustomElement id="el" custom-attr="test"></CustomElement>"#,
/// );
/// ```
///
/// ## Generated With Custom Attributes
///
/// ```rust
/// use html_node::typed::{self, TypedAttributes};
///
/// #[derive(Debug, Clone, Default)]
/// struct CustomElementAttributes {
/// custom_attr: Option<Option<String>>,
/// }
///
/// impl TypedAttributes for CustomElementAttributes {
/// fn into_attributes(self) -> Vec<(String, Option<String>)> {
/// vec![self.custom_attr.map(|v| ("custom-attr".into(), v))]
/// .into_iter()
/// .flatten()
/// .collect()
/// }
/// }
///
/// typed::element! {
/// CustomElement [CustomElementAttributes]
/// }
///
/// // note that global attributes like `id` will not be allowed here
/// // because they are not defined in `CustomElementAttributes`.
/// assert_eq!(
/// typed::html!(<CustomElement custom-attr="test" />).to_string(),
/// r#"<CustomElement custom-attr="test"></CustomElement>"#,
/// );
/// ```
pub use html_node_core::typed_element as element;
/// Make many typed elements.
///
/// This uses the same syntax as [`element!`], but repeated and seperated
/// by semicolons (`;`).
pub use html_node_core::typed_elements as elements;
/// Make a typed HTML node.
///
/// # Examples
///
/// ## Passing Type-Checking
///
/// ```rust
/// use html_node::typed::{self, elements::*};
///
/// let html = typed::html! {
/// <div class="cool" id="hello-world" data-my-attr="hello" aria-label="world">
/// "Hello, world!"
/// </div>
/// };
///
/// let expected = "\
/// <div class=\"cool\" id=\"hello-world\" data-my-attr=\"hello\" aria-label=\"world\">\
/// Hello, world!\
/// </div>\
/// ";
///
/// assert_eq!(html.to_string(), expected);
/// ```
///
/// ## Failing Type-Checking
///
/// ```compile_fail
/// use html_node::typed::{self, elements::*};
///
/// let html = typed::html! {
/// // ERROR: struct `html_node::typed::elements::DivAttributes` has no field named `my_attr`
/// <div class="cool" id="hello-world" my-attr="hello">
/// {text!("Hello, world!")}
/// </div>
/// };
/// ```
pub use html_node_macro::typed_html as html;