diff --git a/CHANGELOG.md b/CHANGELOG.md index 033eb1a..5f9dfd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,23 @@ A dependency update to an incompatible version is considered a breaking change. ## Unreleased +## v0.2.0 - 2025-01-01 + +### Changed + +- **(breaking)** Updated `axum-core` dependency to `0.5.0` +- Relaxed lower bound on `http` dependency to `1.0.0` + +## v0.1.3 - 2024-12-21 + +### Added + +- `html::attr::Rel` + +### Fixed + +- Rendering of HTML comments + ## v0.1.2 - 2024-12-14 ### Added diff --git a/Cargo.toml b/Cargo.toml index 55f6be4..b976823 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "el" -version = "0.1.2" -edition = "2021" +version = "0.2.0" +edition = "2024" authors = ["Garmelon "] description = "Write and manipulate HTML elements as data" -repository = "https://github.com/Garmelon/el" +repository = "https://git.plugh.de/Garmelon/el" license = "MIT OR Apache-2.0" keywords = ["html", "svg", "mathml", "hiccup"] categories = ["web-programming", "template-engine"] @@ -13,16 +13,18 @@ categories = ["web-programming", "template-engine"] axum = ["dep:axum-core", "dep:http"] [dependencies] -axum-core = { version = "0.4.5", optional = true } -http = { version = "1.1.0", optional = true } +axum-core = { version = "0.5.0", optional = true } +http = { version = "1.0.0", optional = true } [lints] rust.unsafe_code = { level = "forbid", priority = 1 } # Lint groups -rust.deprecated_safe = "warn" -rust.future_incompatible = "warn" -rust.keyword_idents = "warn" -rust.rust_2018_idioms = "warn" +rust.deprecated-safe = "warn" +rust.future-incompatible = "warn" +rust.keyword-idents = "warn" +rust.nonstandard-style = "warn" +rust.refining-impl-trait = "warn" +rust.rust-2018-idioms = "warn" rust.unused = "warn" # Individual lints rust.let_underscore_drop = "warn" diff --git a/README.md b/README.md index d269903..842db85 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ and named after a small helper function I once wrote in JS. ## Show me a simple example ```rs -use el::{Attr, Render, html::*}; +use el::{Render, html::*}; let page: String = html(( head(( @@ -35,7 +35,7 @@ See the top-level crate documentation for more info. ## But what about that small helper function? -Here it is in full, for posteriority: +Here it is in full, for posterity: ```js function el(name, attributes, ...children) { diff --git a/src/axum.rs b/src/axum.rs index 44059bb..d0b7c64 100644 --- a/src/axum.rs +++ b/src/axum.rs @@ -1,5 +1,5 @@ use axum_core::response::IntoResponse; -use http::{header, HeaderValue, StatusCode}; +use http::{HeaderValue, StatusCode, header}; use crate::{Document, Render}; diff --git a/src/check.rs b/src/check.rs index c4f3387..01f3a59 100644 --- a/src/check.rs +++ b/src/check.rs @@ -58,10 +58,9 @@ pub fn is_valid_raw_text(tag_name: &str, text: &str) -> bool { // "[...] followed by characters that case-insensitively match the tag // name of the element [...]" // - // Note: Since we know that tag names are ascii-only, we can convert - // both to lowercase for a case-insensitive comparison without weird - // unicode shenanigans. - if potential_tag_name.to_ascii_lowercase() != tag_name.to_ascii_lowercase() { + // Note: Since we know that tag names are ascii-only, we can use an + // ASCII-based case insensitive comparison without unicode shenanigans. + if !potential_tag_name.eq_ignore_ascii_case(tag_name) { continue; } diff --git a/src/element.rs b/src/element.rs index b8fd828..b2b12e0 100644 --- a/src/element.rs +++ b/src/element.rs @@ -1,4 +1,4 @@ -use std::collections::{btree_map::Entry, BTreeMap, HashMap}; +use std::collections::{BTreeMap, HashMap, btree_map::Entry}; /// The kind of an element. /// @@ -501,8 +501,12 @@ element_component_tuple!(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11); element_component_tuple!(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12); element_component_tuple!(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13); element_component_tuple!(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14); -element_component_tuple!(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15); -element_component_tuple!(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16); +element_component_tuple!( + C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15 +); +element_component_tuple!( + C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16 +); /// A full HTML document including doctype. /// diff --git a/src/html/attr.rs b/src/html/attr.rs index 636a552..7a6b71a 100644 --- a/src/html/attr.rs +++ b/src/html/attr.rs @@ -107,6 +107,35 @@ macro_rules! attr_enum { } } }; + ( + $name:ident as $article:ident $actual:expr, separated by $separator:expr; + at $url:expr; + $( $valname:ident => $valstr:expr, )* + ) => { + #[doc = concat!("Create (or append to) ", stringify!($article), " `", $actual, "` attribute")] + #[doc = concat!("(", $url, ").")] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum $name { + $( + #[doc = concat!("The value `", stringify!($valstr), "`.")] + $valname, + )* + } + + impl fmt::Display for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + $( Self::$valname => $valstr.fmt(f), )* + } + } + } + + impl ElementComponent for $name { + fn add_to_element(self, element: &mut Element) { + Attr::append($actual, self, $separator).add_to_element(element); + } + } + }; } //////////////// @@ -757,6 +786,40 @@ attr_enum! { UnsafeUrl => "unsafe-url", } +attr_enum! { + Rel as a "rel", separated by " "; + at url!(normal, "rel"); + Alternate => "alternate", + Author => "author", + Bookmark => "bookmark", + Canonical => "canonical", + DnsPrefetch => "dns-prefetch", + External => "external", + Expect => "expect", + Help => "help", + Icon => "icon", + License => "license", + Manifest => "manifest", + Me => "me", + Modulepreload => "modulepreload", + Next => "next", + Nofollow => "nofollow", + Noopener => "noopener", + Noreferrer => "noreferrer", + Opener => "opener", + Pingback => "pingback", + Preconnect => "preconnect", + Prefetch => "prefetch", + Preload => "preload", + Prerender => "prerender", + Prev => "prev", + PrivacyPolicy => "privacy-policy", + Search => "search", + Stylesheet => "stylesheet", + Tag => "tag", + TermsOfService => "terms-of-service", +} + attr_append! { rel as a "rel", separated by " "; at url!(normal, "rel"); diff --git a/src/lib.rs b/src/lib.rs index fa3ed62..bf907f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ //! ## Usage example //! //! ``` -//! use el::{Attr, Render, html::*}; +//! use el::{Render, html::*}; //! //! let page: String = html(( //! head(( @@ -84,7 +84,7 @@ pub use self::{element::*, render::*}; #[cfg(test)] mod tests { - use crate::{html::*, Attr, Element, Render}; + use crate::{Attr, Content, Element, Render, html::*}; #[test] fn simple_website() { @@ -130,9 +130,11 @@ mod tests { assert!(script("hello world").render_to_string().is_err()); - assert!(script("hello "#, ); } + + #[test] + fn comments() { + assert_eq!( + html((" ", Content::comment("abc"))) + .render_to_string() + .unwrap(), + r#"<!--abc--> "#, + ); + + assert_eq!( + html(Content::comment("Hello !")) + .render_to_string() + .unwrap(), + r#""#, + ); + + assert_eq!( + html(Content::comment("->"#, + ); + } } diff --git a/src/render.rs b/src/render.rs index 52770b5..1109626 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,9 +1,8 @@ use std::{error, fmt}; use crate::{ - check, + Document, check, element::{Content, Element, ElementKind}, - Document, }; /// The cause of an [`Error`]. @@ -246,6 +245,8 @@ fn render_text(w: &mut W, text: &str) -> Result<()> { } fn render_comment(w: &mut W, text: &str) -> Result<()> { + write!(w, "")?; Ok(()) }