//! # el //! //! Write, modify, and safely render HTML elements as simple data structures. //! //! This library is inspired by [hiccup] and named after a small helper function //! I once wrote in JS. //! //! [hiccup]: https://github.com/weavejester/hiccup //! //! ## Library overview //! //! The basic data structure is the [`Element`], which can be rendered to a //! [`String`] using the [`Render`] trait. Custom elements can be constructed //! using [`Element::normal`] or [`Element::new`]. Once constructed, elements //! can be modified by accessing their fields or using the [`Element::add`] or //! [`Element::with`] methods, though this is usually not necessary. //! //! Constructor functions for all (non-deprecated) HTML tags can be found in the //! [`html`] module, SVG tags in [`svg`] and MathML tags in [`mathml`]. These //! three modules are designed to be wildcard-included for more concise code, //! either on a per-function or per-file basis. //! //! Element construction uses the [`ElementComponent`] trait, which represents //! not only element contents but also attributes. Tuples, arrays, [`Vec`], //! [`Option`], and [`Result`] can be used to combine components. The order of //! content components is preserved. To set attributes, include [`Attr`] values //! as components. //! //! If you want to render an entire web page, wrap an [`html::html`] element in //! a [`Document`]. When rendered, documents include the `` //! annotation required by the standard. //! //! ## Usage example //! //! ``` //! use el::{Attr, Render, html::*}; //! //! let page: String = html(( //! head(( //! meta(Attr::new("charset", "utf-8")), //! meta(( //! Attr::new("name", "viewport"), //! Attr::new("content", "width=device-width, initial-scale=1"), //! )), //! title("Example page"), //! )), //! body(( //! h1((Attr::id("heading"), "Example page")), //! p(("This is an example for a ", em("simple"), " web page.")), //! )), //! )) //! .into_document() //! .render_to_string() //! .unwrap(); //! ``` //! //! ## Axum support //! //! The [axum] crate is supported via the optional `axum` feature flag. When it //! is enabled, [`Document`] implements axum's `IntoResponse` trait and can be //! returned directly from handlers. In order to prevent accidentally returning //! incomplete HTML documents, [`Element`] does not implement `IntoResponse`. //! //! ```toml //! [dependencies] //! el = { version = "...", features = ["axum"] } //! ``` //! //! [axum]: https://crates.io/crates/axum //! //! ## But what about that small helper function? //! //! See the readme for more details. #[cfg(feature = "axum")] mod axum; mod check; mod element; pub mod html; pub mod mathml; mod render; pub mod svg; pub use self::{element::*, render::*}; #[cfg(test)] mod tests { use crate::{html::*, Attr, Element, Render}; #[test] fn simple_website() { let page = html(( head(title("Hello")), body((h1("Hello"), p(("Hello ", em("world"), "!")))), )) .into_document() .render_to_string() .unwrap(); assert_eq!( page, concat!( "", "
Hello world!
", "", ), ); } #[test] fn void_elements() { // Difference between void and non-void assert_eq!(head(()).render_to_string().unwrap(), ""); assert_eq!(input(()).render_to_string().unwrap(), ""); // Void elements must not contain any children assert!(input(p(())).render_to_string().is_err()); } #[test] fn raw_text_elements() { assert_eq!( script("foo ", ); println!("{:?}", script("hello world").render_to_string(),); assert!(script("hello world").render_to_string().is_err()); assert!(script("hello & bar").render_to_string().unwrap(), "", ); assert!(textarea(p(())).render_to_string().is_err()); } #[test] fn attributes() { assert_eq!( input(( Attr::new("name", "tentacles"), Attr::new("type", "number"), Attr::new("min", 10), Attr::new("max", 100), )) .render_to_string() .unwrap(), r#""#, ); assert_eq!( input((Attr::new("name", "horns"), Attr::yes("checked"))) .render_to_string() .unwrap(), r#""#, ); } #[test] fn always_lowercase() { assert_eq!( Element::normal("HTML") .with(Attr::new("LANG", "EN")) .render_to_string() .unwrap(), r#""#, ); } }