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 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
#![recursion_limit = "128"]
//! This crate provides the `html!` macro for building fully type checked HTML
//! documents inside your Rust code using roughly [JSX] compatible syntax.
//!
//! This crate is a fork of the great [Bodil Stokke's] [typed-html] crate. Opted
//! for a fork instead of maintainership because not currently intending to use or
//! maintain the Wasm compatibility (for now).
//!
//! [Bodil Stokke's]: https://github.com/bodil
//! [typed-html]: https://github.com/bodil/typed-html
//!
//! # Quick Preview
//!
//! ```
//! # #![recursion_limit = "128"]
//! # use axohtml::html;
//! # use axohtml::dom::{DOMTree, VNode};
//! # use axohtml::types::Metadata;
//! # fn main() {
//! let mut doc: DOMTree<String> = html!(
//! <html>
//! <head>
//! <title>"Hello Axo"</title>
//! <meta name=Metadata::Author content="Axo Developer Co."/>
//! </head>
//! <body>
//! <h1>">o_o<"</h1>
//! <p class="official">
//! "The tool company for tool companies"
//! </p>
//! { (0..3).map(|_| html!(
//! <p class="emphasis">
//! ">o_o<"
//! </p>
//! )) }
//! <p class="citation-needed">
//! "Every company should be a developer experience company."
//! </p>
//! </body>
//! </html>
//! );
//! let doc_str = doc.to_string();
//! # }
//! ```
//!
//! # Syntax
//!
//! This macro largely follows [JSX] syntax, but with some differences:
//!
//! * Text nodes must be quoted, because there's only so much Rust's tokeniser can
//! handle outside string literals. So, instead of `<p>Hello</p>`, you need to
//! write `<p>"Hello"</p>`. (The parser will throw an error asking you to do this
//! if you forget.)
//! * Element attributes will accept simple Rust expressions, but the parser has
//! its limits, as it's not a full Rust parser. You can use literals,
//! variables, dotted properties, type constructors and single function or
//! method calls. If you use something the parser isn't currently capable of
//! handling, it will complain. You can put braces or parentheses around the
//! expression if the parser doesn't understand
//! it. You can use any Rust code inside a brace or parenthesis block.
//!
//! # Valid HTML5
//!
//! The macro will only accept valid HTML5 tags, with no tags or attributes marked
//! experimental or obsolete. If it won't accept something you want it to accept, we
//! can discuss it over a pull request (experimental tags and attributes, in
//! particular, are mostly omitted just for brevity, and you're welcome to implement
//! them).
//!
//! The structure validation is simplistic by necessity, as it defers to the type
//! system: a few elements will have one or more required children, and any element
//! which accepts children will have a restriction on the type of the children,
//! usually a broad group as defined by the HTML spec. Many elements have
//! restrictions on children of children, or require a particular ordering of
//! optional elements, which isn't currently validated.
//!
//! # Attribute Values
//!
//! Brace blocks in the attribute value position should return the expected type for
//! the attribute. The type checker will complain if you return an unsupported type.
//! You can also use literals or a few simple Rust expressions as attribute values
//! (see the Syntax section above).
//!
//! The `html!` macro will add an [`.into()`][Into::into] call to the value
//! expression, so that you can use any type that has an [`Into<A>`] trait
//! defined for the actual attribute type `A`.
//!
//! As a special case, if you use a string literal, the macro will instead use the
//! [`FromStr<A>`][std::str::FromStr] trait to try and parse the string literal into the
//! expected type. This is extremely useful for eg. CSS classes, letting you type
//! `class="css-class-1 css-class-2"` instead of going to the trouble of
//! constructing a [`SpacedSet<Class>`][types::SpacedSet]. The big caveat for this:
//! currently, the macro is not able to validate the string at compile time, and the
//! conversion will panic at runtime if the string is invalid.
//!
//! ## Example
//!
//! ```
//! # use std::convert::{TryFrom, TryInto};
//! # use axohtml::html;
//! # use axohtml::dom::DOMTree;
//! # use axohtml::types::{Class, SpacedSet};
//! # fn main() -> Result<(), &'static str> {
//! let classList: SpacedSet<Class> = ["foo", "bar", "baz"].try_into()?;
//! # let doc: DOMTree<String> =
//! html!(
//! <div>
//! <div class="foo bar baz" /> // parses a string literal
//! <div class=["foo", "bar", "baz"] /> // uses From<[&str, &str, &str]>
//! <div class=classList /> // uses a variable in scope
//! <div class={ // evaluates a code block
//! SpacedSet::try_from(["foo", "bar", "baz"])?
//! } />
//! </div>
//! )
//! # ; Ok(()) }
//! ```
//!
//! # Generated Nodes
//!
//! Brace blocks in the child node position are expected to return an
//! [`IntoIterator`] of [`DOMTree`][dom::DOMTree]s. You can return single
//! elements or text nodes, as they both implement `IntoIterator` for themselves.
//! The macro will consume this iterator at runtime and insert the generated nodes
//! as children in the expected position.
//!
//! ## Example
//!
//! ```
//! # use axohtml::{html, text};
//! # use axohtml::dom::DOMTree;
//! # fn main() {
//! # let doc: DOMTree<String> =
//! html!(
//! <ul>
//! { (1..=5).map(|i| html!(
//! <li>{ text!("{}", i) }</li>
//! )) }
//! </ul>
//! )
//! # ;}
//! ```
//!
//! # Rendering
//!
//! You have two options for actually producing something useful from the DOM tree
//! that comes out of the macro.
//!
//! ## Render to a string
//!
//! The DOM tree data structure implements [`Display`], so you can call
//! [`to_string()`][std::string::ToString::to_string] on it to render it to a [`String`]. If you
//! plan to do this, the type of the tree should be [`DOMTree<String>`][dom::DOMTree] to
//! ensure you're not using any event handlers that can't be printed.
//!
//! ```
//! # use axohtml::html;
//! # use axohtml::dom::DOMTree;
//! # fn main() {
//! let doc: DOMTree<String> = html!(
//! <p>"Hello Axo"</p>
//! );
//! let doc_str = doc.to_string();
//! assert_eq!("<p>Hello Axo</p>", doc_str);
//! # }
//! ```
//!
//! ## Render to a virtual DOM
//!
//! The DOM tree structure also implements a method called `vnode()`, which renders
//! the tree to a tree of [`VNode`][dom::VNode]s, which is a mirror of the generated tree
//! with every attribute value rendered into `String`s. You can walk this virtual
//! DOM tree and pass it on to your favourite virtual DOM system.
//!
//! # License
//!
//! Copyright 2018 Bodil Stokke, 2022 Axo Developer Co.
//!
//! This software is subject to the terms of the Mozilla Public License, v. 2.0. If
//! a copy of the MPL was not distributed with this file, You can obtain one at
//! <http://mozilla.org/MPL/2.0/>.
//!
//! [JSX]: https://reactjs.org/docs/introducing-jsx.html
pub extern crate htmlescape;
use std::fmt::Display;
pub use axohtml_macros::html;
pub mod dom;
pub mod elements;
pub mod events;
pub mod types;
/// Marker trait for outputs
pub trait OutputType {
/// The type that contains events for this output.
type Events: Default + Display + Send;
/// The type of event targets for this output.
type EventTarget: Send;
/// The type that's returned from attaching an event listener to a target.
type EventListenerHandle: Send;
}
/// String output
impl OutputType for String {
type Events = events::Events<String>;
type EventTarget = ();
type EventListenerHandle = ();
}
pub fn escape_html_attribute(html_attr: String) -> String {
// Even though the code is quoting the variables with a double quote, escape all known quoting chars
html_attr
.replace('\"', """)
.replace('\'', "'")
.replace('`', "`")
}