summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2023-04-09 10:55:01 +0300
committerLars Wirzenius <liw@liw.fi>2023-04-09 10:55:01 +0300
commitc55a41936938dce5adc22dece6b7ea02e14980ae (patch)
treef0651f38922880f917e455f80d682e380d1393c1
parente8ca964f345d716cd776f3d54676d226b6e17b89 (diff)
downloadhtml-page-c55a41936938dce5adc22dece6b7ea02e14980ae.tar.gz
docs: add doc comments for all public symbols
Sponsored-by: author
-rw-r--r--src/lib.rs75
1 files changed, 60 insertions, 15 deletions
diff --git a/src/lib.rs b/src/lib.rs
index a7851fa..88d1e90 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,13 @@
-//! Construct and manipulate an HTML page represented using Rust types.
+//! Construct and manipulate an HTML element represented using Rust types.
+//!
+//! Conceptually, an element has a tag, optional attributes, and
+//! optional children. A child can consist of non-HTML text, or be an
+//! element of its own.
+//!
+//! This crate aims to follow the WhatWG specification at
+//! <https://html.spec.whatwg.org/>.
-//! Represent an HTML element.
+#![deny(missing_docs)]
use html_escape::{encode_double_quoted_attribute, encode_safe};
use std::collections::HashMap;
@@ -9,8 +16,10 @@ use std::fmt::{Display, Formatter};
/// The tag of an HTML5 element.
///
/// Note that we only support HTML5 elements, as listed on
-/// <https://html.spec.whatwg.org/multipage/#toc-semantics/>.
+/// <https://html.spec.whatwg.org//>.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[allow(missing_docs)] // the variants are just element names, no need
+ // to document each separately
pub enum Tag {
A,
Abbr,
@@ -171,12 +180,28 @@ impl Display for Attributes {
}
}
+/// The value of an element attribute.
+///
+/// Attributes may be "boolean" (just the name of an attribute), or a
+/// key/value pair, where the value is a string. Technically, in HTML,
+/// a boolean attribute with a true value can be expressed as a
+/// key/value pair with a value that is an empty string or the name of
+/// the attribute, but in this representation we make it more
+/// explicit.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AttributeValue {
+ /// The value of a key/value attribute.
String(String),
+ /// A boolean attribute. It the attribute is present, the value is
+ /// true.
Boolean,
}
+/// An HTML element.
+///
+/// The element has a [`Tag`], possibly some attributes, and possibly
+/// some children. It may also have a location: this is used when the
+/// element is constructed by parsing some input value.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Element {
loc: Option<(usize, usize)>,
@@ -186,6 +211,7 @@ pub struct Element {
}
impl Element {
+ /// Create a new element, with a given tag.
pub fn new(tag: Tag) -> Self {
Self {
tag,
@@ -195,47 +221,61 @@ impl Element {
}
}
+ /// Set the location of an element in a source file.
pub fn with_location(mut self, line: usize, col: usize) -> Self {
self.loc = Some((line, col));
self
}
+ /// Return the [`Tag`] of the element.
pub fn tag(&self) -> Tag {
self.tag
}
+ /// Return the location of the element.
pub fn location(&self) -> Option<(usize, usize)> {
self.loc
}
+ /// Return an iterator over the names of the attributes of an element.
pub fn attributes(&self) -> impl Iterator<Item = &str> {
self.attrs.names()
}
+ /// Return the value of an attribute, if the attribute is set.
+ /// Otherwise, return `None`.
pub fn attribute(&self, name: &str) -> Option<&AttributeValue> {
self.attrs.get(name)
}
+ /// Set a key/value attribute. If the attribute was already set,
+ /// change the value it has.
pub fn set_attribute(&mut self, name: &str, value: &str) {
self.attrs.set(name, value);
}
+ /// Set a boolean attribute.
pub fn set_boolean_attribute(&mut self, name: &str) {
self.attrs.set_boolean(name);
}
+ /// Remove an attribute, which can be key/value or boolean.
pub fn unset_attribute(&mut self, name: &str) {
self.attrs.unset(name);
}
- pub fn children(&self) -> &[Content] {
- &self.children
+ /// Append text to element. It will be escaped, if needed, when
+ /// the element is serialized.
+ pub fn push_text(&mut self, text: &str) {
+ self.children.push(Content::text(text));
}
- pub fn push_child(&mut self, child: Content) {
- self.children.push(child);
+ /// Append a child element to this element.
+ pub fn push_child(&mut self, child: &Element) {
+ self.children.push(Content::element(child));
}
+ /// Serialize an element into HTML.
pub fn serialize(&self) -> String {
format!("{}", self)
}
@@ -256,17 +296,22 @@ impl Display for Element {
}
}
+/// Represent content in HTML.
#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum Content {
+enum Content {
+ /// Non-HTML text.
Text(String),
+ /// An HTML element.
Element(Element),
}
impl Content {
+ /// Create a new [`Content::Text`].
pub fn text(s: &str) -> Self {
Self::Text(s.into())
}
+ /// Create a new [`Content::Element`].
pub fn element(e: &Element) -> Self {
Self::Element(e.clone())
}
@@ -338,15 +383,15 @@ mod test {
#[test]
fn element_has_no_children_initially() {
let e = Element::new(Tag::P);
- assert!(e.children().is_empty());
+ assert!(e.children.is_empty());
}
#[test]
fn add_child_to_element() {
let mut e = Element::new(Tag::P);
let child = Content::text("foo");
- e.push_child(child.clone());
- assert_eq!(e.children(), &[child]);
+ e.push_text("foo");
+ assert_eq!(e.children, &[child]);
}
#[test]
@@ -385,17 +430,17 @@ mod test {
#[test]
fn element_can_be_serialized() {
let mut e = Element::new(Tag::P);
- e.push_child(Content::text("hello "));
+ e.push_text("hello ");
let mut world = Element::new(Tag::B);
- world.push_child(Content::text("world"));
- e.push_child(Content::Element(world));
+ world.push_text("world");
+ e.push_child(&world);
assert_eq!(e.serialize(), "<P>hello <B>world</B></P>");
}
#[test]
fn dangerous_text_is_escaped() {
let mut e = Element::new(Tag::P);
- e.push_child(Content::text("hello <world>"));
+ e.push_text("hello <world>");
assert_eq!(e.serialize(), "<P>hello &lt;world&gt;</P>");
}
}