Compare commits
No commits in common. "master" and "v0.1.0" have entirely different histories.
10 changed files with 62 additions and 1323 deletions
42
CHANGELOG.md
42
CHANGELOG.md
|
|
@ -6,7 +6,7 @@ A dependency update to an incompatible version is considered a breaking change.
|
||||||
|
|
||||||
## Releasing a new version
|
## Releasing a new version
|
||||||
|
|
||||||
0. Ensure tests don't fail
|
0. Ensure tests don't fail *ahem*
|
||||||
1. Update dependencies in a separate commit, if necessary
|
1. Update dependencies in a separate commit, if necessary
|
||||||
2. Set new version number in [`Cargo.toml`](Cargo.toml)
|
2. Set new version number in [`Cargo.toml`](Cargo.toml)
|
||||||
3. Add new section in this changelog
|
3. Add new section in this changelog
|
||||||
|
|
@ -17,46 +17,6 @@ A dependency update to an incompatible version is considered a breaking change.
|
||||||
|
|
||||||
## Unreleased
|
## 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
|
|
||||||
|
|
||||||
- `Attr::set`
|
|
||||||
- `html::attr`
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- `Attr::new` in favor of `Attr::set`
|
|
||||||
- `Attr::id` in favor of `html::attr::id`
|
|
||||||
- `Attr::class` in favor of `html::attr::class`
|
|
||||||
- `Attr::style` in favor of `html::attr::style`
|
|
||||||
- `Attr::data` in favor of `html::attr::data_x`
|
|
||||||
|
|
||||||
## v0.1.1 - 2024-12-08
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `Element::into_document`
|
|
||||||
- `impl From<&String> for Content`
|
|
||||||
- Eponymous JS helper function in readme
|
|
||||||
|
|
||||||
## v0.1.0 - 2024-12-02
|
## v0.1.0 - 2024-12-02
|
||||||
|
|
||||||
Initial release
|
Initial release
|
||||||
|
|
|
||||||
20
Cargo.toml
20
Cargo.toml
|
|
@ -1,10 +1,10 @@
|
||||||
[package]
|
[package]
|
||||||
name = "el"
|
name = "el"
|
||||||
version = "0.2.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
authors = ["Garmelon <garmelon@plugh.de>"]
|
authors = ["Garmelon <garmelon@plugh.de>"]
|
||||||
description = "Write and manipulate HTML elements as data"
|
description = "Write and manipulate HTML elements as data"
|
||||||
repository = "https://git.plugh.de/Garmelon/el"
|
repository = "https://github.com/Garmelon/el"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
keywords = ["html", "svg", "mathml", "hiccup"]
|
keywords = ["html", "svg", "mathml", "hiccup"]
|
||||||
categories = ["web-programming", "template-engine"]
|
categories = ["web-programming", "template-engine"]
|
||||||
|
|
@ -13,18 +13,16 @@ categories = ["web-programming", "template-engine"]
|
||||||
axum = ["dep:axum-core", "dep:http"]
|
axum = ["dep:axum-core", "dep:http"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum-core = { version = "0.5.0", optional = true }
|
axum-core = { version = "0.4.5", optional = true }
|
||||||
http = { version = "1.0.0", optional = true }
|
http = { version = "1.1.0", optional = true }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
rust.unsafe_code = { level = "forbid", priority = 1 }
|
rust.unsafe_code = { level = "forbid", priority = 1 }
|
||||||
# Lint groups
|
# Lint groups
|
||||||
rust.deprecated-safe = "warn"
|
rust.deprecated_safe = "warn"
|
||||||
rust.future-incompatible = "warn"
|
rust.future_incompatible = "warn"
|
||||||
rust.keyword-idents = "warn"
|
rust.keyword_idents = "warn"
|
||||||
rust.nonstandard-style = "warn"
|
rust.rust_2018_idioms = "warn"
|
||||||
rust.refining-impl-trait = "warn"
|
|
||||||
rust.rust-2018-idioms = "warn"
|
|
||||||
rust.unused = "warn"
|
rust.unused = "warn"
|
||||||
# Individual lints
|
# Individual lints
|
||||||
rust.let_underscore_drop = "warn"
|
rust.let_underscore_drop = "warn"
|
||||||
|
|
|
||||||
54
README.md
54
README.md
|
|
@ -1,70 +1,34 @@
|
||||||
# el
|
# el
|
||||||
|
|
||||||
`el` is a no-dependencies Rust library for writing, modifying, and safely
|
`el` is a Rust library for writing, modifying, and safely rendering HTML
|
||||||
rendering HTML elements as simple data structures. It is inspired by [hiccup]
|
elements as simple data structures. It is inspired by [hiccup] and named after a
|
||||||
and named after a small helper function I once wrote in JS.
|
small helper function I once wrote in JS.
|
||||||
|
|
||||||
[hiccup]: https://github.com/weavejester/hiccup
|
[hiccup]: https://github.com/weavejester/hiccup
|
||||||
|
|
||||||
## Show me a simple example
|
## Usage example
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
use el::{Render, html::*};
|
use el::{Attr, Render, html::*};
|
||||||
|
|
||||||
let page: String = html((
|
let page: String = html((
|
||||||
head((
|
head((
|
||||||
|
meta(Attr::new("charset", "utf-8")),
|
||||||
meta((
|
meta((
|
||||||
attr::name("viewport"),
|
Attr::new("name", "viewport"),
|
||||||
attr::content("width=device-width, initial-scale=1"),
|
Attr::new("content", "width=device-width, initial-scale=1"),
|
||||||
)),
|
)),
|
||||||
title("Example page"),
|
title("Example page"),
|
||||||
)),
|
)),
|
||||||
body((
|
body((
|
||||||
h1((attr::id("heading"), "Example page")),
|
h1((Attr::id("heading"), "Example page")),
|
||||||
p(("This is an example for a ", em("simple"), " web page.")),
|
p(("This is an example for a ", em("simple"), " web page.")),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
.into_document()
|
|
||||||
.render_to_string()
|
.render_to_string()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
```
|
```
|
||||||
|
|
||||||
## What now?
|
|
||||||
|
|
||||||
See the top-level crate documentation for more info.
|
|
||||||
|
|
||||||
## But what about that small helper function?
|
|
||||||
|
|
||||||
Here it is in full, for posterity:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function el(name, attributes, ...children) {
|
|
||||||
const element = document.createElement(name);
|
|
||||||
for (const [name, value] of Object.entries(attributes))
|
|
||||||
element.setAttribute(name, value);
|
|
||||||
element.append(...children);
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Use it like so:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const page = el("html", {},
|
|
||||||
el("head", {},
|
|
||||||
el("meta", {
|
|
||||||
name: "viewport",
|
|
||||||
content: "width=device-width, initial-scale=1",
|
|
||||||
}),
|
|
||||||
el("title", {}, "Example page"),
|
|
||||||
),
|
|
||||||
el("body", {},
|
|
||||||
el("h1", { id: "heading" }, "Example page"),
|
|
||||||
el("p", {}, "This is an example for a ", el("em", {}, "simple"), " web page."),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This entire project is dual-licensed under the [Apache 2.0] and [MIT] licenses.
|
This entire project is dual-licensed under the [Apache 2.0] and [MIT] licenses.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use axum_core::response::IntoResponse;
|
use axum_core::response::IntoResponse;
|
||||||
use http::{HeaderValue, StatusCode, header};
|
use http::{header, HeaderValue, StatusCode};
|
||||||
|
|
||||||
use crate::{Document, Render};
|
use crate::{Document, Render};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,10 @@ pub fn is_valid_raw_text(tag_name: &str, text: &str) -> bool {
|
||||||
// "[...] followed by characters that case-insensitively match the tag
|
// "[...] followed by characters that case-insensitively match the tag
|
||||||
// name of the element [...]"
|
// name of the element [...]"
|
||||||
//
|
//
|
||||||
// Note: Since we know that tag names are ascii-only, we can use an
|
// Note: Since we know that tag names are ascii-only, we can convert
|
||||||
// ASCII-based case insensitive comparison without unicode shenanigans.
|
// both to lowercase for a case-insensitive comparison without weird
|
||||||
if !potential_tag_name.eq_ignore_ascii_case(tag_name) {
|
// unicode shenanigans.
|
||||||
|
if potential_tag_name.to_ascii_lowercase() != tag_name.to_ascii_lowercase() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::collections::{BTreeMap, HashMap, btree_map::Entry};
|
use std::collections::{btree_map::Entry, BTreeMap, HashMap};
|
||||||
|
|
||||||
/// The kind of an element.
|
/// The kind of an element.
|
||||||
///
|
///
|
||||||
|
|
@ -87,12 +87,6 @@ impl Content {
|
||||||
|
|
||||||
impl From<String> for Content {
|
impl From<String> for Content {
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Self::Text(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&String> for Content {
|
|
||||||
fn from(value: &String) -> Self {
|
|
||||||
Self::text(value)
|
Self::text(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -245,14 +239,6 @@ impl Element {
|
||||||
self.add(c);
|
self.add(c);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this element into a [`Document`].
|
|
||||||
///
|
|
||||||
/// This function is equivalent to calling `self.into()` but may be more
|
|
||||||
/// convenient in some cases.
|
|
||||||
pub fn into_document(self) -> Document {
|
|
||||||
self.into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component can add itself to an [`Element`] by modifying it.
|
/// A component can add itself to an [`Element`] by modifying it.
|
||||||
|
|
@ -301,7 +287,7 @@ impl Attr {
|
||||||
/// When this attribute is added to an [`Element`] through
|
/// When this attribute is added to an [`Element`] through
|
||||||
/// [`ElementComponent::add_to_element`] and an attribute of the same name
|
/// [`ElementComponent::add_to_element`] and an attribute of the same name
|
||||||
/// already exists, it replaces that attribute's value.
|
/// already exists, it replaces that attribute's value.
|
||||||
pub fn set(name: impl ToString, value: impl ToString) -> Self {
|
pub fn new(name: impl ToString, value: impl ToString) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
value: value.to_string(),
|
value: value.to_string(),
|
||||||
|
|
@ -309,16 +295,6 @@ impl Attr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create or replace an attribute.
|
|
||||||
///
|
|
||||||
/// When this attribute is added to an [`Element`] through
|
|
||||||
/// [`ElementComponent::add_to_element`] and an attribute of the same name
|
|
||||||
/// already exists, it replaces that attribute's value.
|
|
||||||
#[deprecated = "use `Attr::set` instead"]
|
|
||||||
pub fn new(name: impl ToString, value: impl ToString) -> Self {
|
|
||||||
Self::set(name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create or append to an attribute.
|
/// Create or append to an attribute.
|
||||||
///
|
///
|
||||||
/// When this attribute is added to an [`Element`] through
|
/// When this attribute is added to an [`Element`] through
|
||||||
|
|
@ -340,31 +316,28 @@ impl Attr {
|
||||||
/// When rendering an empty attribute as HTML, the value can be omitted:
|
/// When rendering an empty attribute as HTML, the value can be omitted:
|
||||||
/// `name=""` is equivalent to just `name`.
|
/// `name=""` is equivalent to just `name`.
|
||||||
pub fn yes(name: impl ToString) -> Self {
|
pub fn yes(name: impl ToString) -> Self {
|
||||||
Self::set(name, "")
|
Self::new(name, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create (or replace) an `id` attribute.
|
/// Create (or replace) an `id` attribute.
|
||||||
///
|
///
|
||||||
/// `Attr::id(id)` is equivalent to `Attr::new("id", id)`.
|
/// `Attr::id(id)` is equivalent to `Attr::new("id", id)`.
|
||||||
#[deprecated = "use `html::attr::id` instead"]
|
|
||||||
pub fn id(id: impl ToString) -> Self {
|
pub fn id(id: impl ToString) -> Self {
|
||||||
Self::set("id", id)
|
Self::new("id", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create (or append to) a `class` attribute.
|
/// Create (or append) to a `class` attribute.
|
||||||
///
|
///
|
||||||
/// `Attr::class(class)` is equivalent to
|
/// `Attr::class(class)` is equivalent to
|
||||||
/// `Attr::append("class", class, " ")`.
|
/// `Attr::append("class", class, " ")`.
|
||||||
#[deprecated = "use `html::attr::class` instead"]
|
|
||||||
pub fn class(class: impl ToString) -> Self {
|
pub fn class(class: impl ToString) -> Self {
|
||||||
Self::append("class", class, " ")
|
Self::append("class", class, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create (or append to) a `style` attribute.
|
/// Create (or append) to a `style` attribute.
|
||||||
///
|
///
|
||||||
/// `Attr::style(style)` is equivalent to
|
/// `Attr::style(style)` is equivalent to
|
||||||
/// `Attr::append("style", style, ";")`.
|
/// `Attr::append("style", style, ";")`.
|
||||||
#[deprecated = "use `html::attr::style` instead"]
|
|
||||||
pub fn style(style: impl ToString) -> Self {
|
pub fn style(style: impl ToString) -> Self {
|
||||||
Self::append("style", style, ";")
|
Self::append("style", style, ";")
|
||||||
}
|
}
|
||||||
|
|
@ -375,9 +348,8 @@ impl Attr {
|
||||||
/// `Attr::new(format!("data-{name}"), value)`.
|
/// `Attr::new(format!("data-{name}"), value)`.
|
||||||
///
|
///
|
||||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*
|
||||||
#[deprecated = "use `html::attr::data_x` instead"]
|
|
||||||
pub fn data(name: impl ToString, value: impl ToString) -> Self {
|
pub fn data(name: impl ToString, value: impl ToString) -> Self {
|
||||||
Self::set(format!("data-{}", name.to_string()), value)
|
Self::new(format!("data-{}", name.to_string()), value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,7 +379,7 @@ impl ElementComponent for Attr {
|
||||||
impl ElementComponent for HashMap<String, String> {
|
impl ElementComponent for HashMap<String, String> {
|
||||||
fn add_to_element(self, element: &mut Element) {
|
fn add_to_element(self, element: &mut Element) {
|
||||||
for (name, value) in self {
|
for (name, value) in self {
|
||||||
Attr::set(name, value).add_to_element(element);
|
Attr::new(name, value).add_to_element(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -415,7 +387,7 @@ impl ElementComponent for HashMap<String, String> {
|
||||||
impl ElementComponent for BTreeMap<String, String> {
|
impl ElementComponent for BTreeMap<String, String> {
|
||||||
fn add_to_element(self, element: &mut Element) {
|
fn add_to_element(self, element: &mut Element) {
|
||||||
for (name, value) in self {
|
for (name, value) in self {
|
||||||
Attr::set(name, value).add_to_element(element);
|
Attr::new(name, value).add_to_element(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -501,12 +473,8 @@ 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);
|
||||||
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);
|
||||||
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);
|
||||||
element_component_tuple!(
|
element_component_tuple!(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15);
|
||||||
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, C16
|
|
||||||
);
|
|
||||||
|
|
||||||
/// A full HTML document including doctype.
|
/// A full HTML document including doctype.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
//! Definitions for HTML elements and attributes
|
//! Definitions for all non-deprecated HTML elements
|
||||||
//! ([MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element)).
|
//! ([MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element)).
|
||||||
//!
|
|
||||||
//! Deprecated HTML elements are not included.
|
|
||||||
|
|
||||||
pub mod attr;
|
|
||||||
|
|
||||||
use crate::{Element, ElementComponent, ElementKind};
|
use crate::{Element, ElementComponent, ElementKind};
|
||||||
|
|
||||||
|
|
|
||||||
1107
src/html/attr.rs
1107
src/html/attr.rs
File diff suppressed because it is too large
Load diff
87
src/lib.rs
87
src/lib.rs
|
|
@ -33,22 +33,22 @@
|
||||||
//! ## Usage example
|
//! ## Usage example
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use el::{Render, html::*};
|
//! use el::{Attr, Render, html::*};
|
||||||
//!
|
//!
|
||||||
//! let page: String = html((
|
//! let page: String = html((
|
||||||
//! head((
|
//! head((
|
||||||
|
//! meta(Attr::new("charset", "utf-8")),
|
||||||
//! meta((
|
//! meta((
|
||||||
//! attr::name("viewport"),
|
//! Attr::new("name", "viewport"),
|
||||||
//! attr::content("width=device-width, initial-scale=1"),
|
//! Attr::new("content", "width=device-width, initial-scale=1"),
|
||||||
//! )),
|
//! )),
|
||||||
//! title("Example page"),
|
//! title("Example page"),
|
||||||
//! )),
|
//! )),
|
||||||
//! body((
|
//! body((
|
||||||
//! h1((attr::id("heading"), "Example page")),
|
//! h1((Attr::id("heading"), "Example page")),
|
||||||
//! p(("This is an example for a ", em("simple"), " web page.")),
|
//! p(("This is an example for a ", em("simple"), " web page.")),
|
||||||
//! )),
|
//! )),
|
||||||
//! ))
|
//! ))
|
||||||
//! .into_document()
|
|
||||||
//! .render_to_string()
|
//! .render_to_string()
|
||||||
//! .unwrap();
|
//! .unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
|
|
@ -66,10 +66,6 @@
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! [axum]: https://crates.io/crates/axum
|
//! [axum]: https://crates.io/crates/axum
|
||||||
//!
|
|
||||||
//! ## But what about that small helper function?
|
|
||||||
//!
|
|
||||||
//! See the readme for more details.
|
|
||||||
|
|
||||||
#[cfg(feature = "axum")]
|
#[cfg(feature = "axum")]
|
||||||
mod axum;
|
mod axum;
|
||||||
|
|
@ -84,20 +80,21 @@ pub use self::{element::*, render::*};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{Attr, Content, Element, Render, html::*};
|
use crate::{html::*, Attr, Content, Element, Render};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_website() {
|
fn simple_website() {
|
||||||
let page = html((
|
let els = [
|
||||||
head(title("Hello")),
|
Content::doctype(),
|
||||||
body((h1("Hello"), p(("Hello ", em("world"), "!")))),
|
html((
|
||||||
))
|
head(title("Hello")),
|
||||||
.into_document()
|
body((h1("Hello"), p(("Hello ", em("world"), "!")))),
|
||||||
.render_to_string()
|
))
|
||||||
.unwrap();
|
.into(),
|
||||||
|
];
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
page,
|
els.render_to_string().unwrap(),
|
||||||
concat!(
|
concat!(
|
||||||
"<!DOCTYPE html><html>",
|
"<!DOCTYPE html><html>",
|
||||||
"<head><title>Hello</title></head>",
|
"<head><title>Hello</title></head>",
|
||||||
|
|
@ -130,11 +127,9 @@ mod tests {
|
||||||
|
|
||||||
assert!(script("hello </script> world").render_to_string().is_err());
|
assert!(script("hello </script> world").render_to_string().is_err());
|
||||||
|
|
||||||
assert!(
|
assert!(script("hello </ScRiPt ... world")
|
||||||
script("hello </ScRiPt ... world")
|
.render_to_string()
|
||||||
.render_to_string()
|
.is_err());
|
||||||
.is_err()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -151,10 +146,10 @@ mod tests {
|
||||||
fn attributes() {
|
fn attributes() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
input((
|
input((
|
||||||
Attr::set("name", "tentacles"),
|
Attr::new("name", "tentacles"),
|
||||||
attr::TypeInput::Number,
|
Attr::new("type", "number"),
|
||||||
attr::min(10),
|
Attr::new("min", 10),
|
||||||
Attr::append("max", 100, "FOOBAA"),
|
Attr::new("max", 100),
|
||||||
))
|
))
|
||||||
.render_to_string()
|
.render_to_string()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
|
@ -162,55 +157,21 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
input((Attr::set("name", "horns"), Attr::yes("checked")))
|
input((Attr::new("name", "horns"), Attr::yes("checked")))
|
||||||
.render_to_string()
|
.render_to_string()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
r#"<input checked name="horns">"#,
|
r#"<input checked name="horns">"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
p((
|
|
||||||
attr::id("foo"),
|
|
||||||
attr::id("bar"),
|
|
||||||
attr::class("foo"),
|
|
||||||
attr::class("bar"),
|
|
||||||
))
|
|
||||||
.render_to_string()
|
|
||||||
.unwrap(),
|
|
||||||
r#"<p class="foo bar" id="bar"></p>"#,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn always_lowercase() {
|
fn always_lowercase() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Element::normal("HTML")
|
Element::normal("HTML")
|
||||||
.with(Attr::set("LANG", "EN"))
|
.with(Attr::new("LANG", "EN"))
|
||||||
.render_to_string()
|
.render_to_string()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
r#"<html lang="EN"></html>"#,
|
r#"<html lang="EN"></html>"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn comments() {
|
|
||||||
assert_eq!(
|
|
||||||
html(("<!--abc--> ", Content::comment("abc")))
|
|
||||||
.render_to_string()
|
|
||||||
.unwrap(),
|
|
||||||
r#"<html><!--abc--> <!--abc--></html>"#,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
html(Content::comment("Hello <!-- world -->!"))
|
|
||||||
.render_to_string()
|
|
||||||
.unwrap(),
|
|
||||||
r#"<html><!--Hello <!== world ==>!--></html>"#,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
html(Content::comment("-><!-")).render_to_string().unwrap(),
|
|
||||||
r#"<html><!-- -><!- --></html>"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use std::{error, fmt};
|
use std::{error, fmt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Document, check,
|
check,
|
||||||
element::{Content, Element, ElementKind},
|
element::{Content, Element, ElementKind},
|
||||||
|
Document,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The cause of an [`Error`].
|
/// The cause of an [`Error`].
|
||||||
|
|
@ -245,8 +246,6 @@ fn render_text<W: fmt::Write>(w: &mut W, text: &str) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_comment<W: fmt::Write>(w: &mut W, text: &str) -> Result<()> {
|
fn render_comment<W: fmt::Write>(w: &mut W, text: &str) -> Result<()> {
|
||||||
write!(w, "<!--")?;
|
|
||||||
|
|
||||||
// A comment...
|
// A comment...
|
||||||
// - must not start with the string ">"
|
// - must not start with the string ">"
|
||||||
// - must not start with the string "->"
|
// - must not start with the string "->"
|
||||||
|
|
@ -270,7 +269,6 @@ fn render_comment<W: fmt::Write>(w: &mut W, text: &str) -> Result<()> {
|
||||||
write!(w, " ")?;
|
write!(w, " ")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(w, "-->")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue