Skip to content

Commit

Permalink
Merge pull request #8 from Nemo157/documentation
Browse files Browse the repository at this point in the history
Complete docs coverage!
  • Loading branch information
critiqjo committed Apr 9, 2016
2 parents 31160b5 + b83593e commit 49de63f
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 38 deletions.
96 changes: 68 additions & 28 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,45 @@ use escape::Escaped;

/// An [HTML attribute](https://www.w3.org/TR/html/syntax.html#attributes-0).
///
/// The name for the attribute will not be validated, you must ensure it meets
/// the requirements specified in the spec yourself.
///
/// The value for the attribute will be escaped automatically. If it is an
/// empty string then the attribute will be written with the 'Empty attribute
/// syntax'.
///
/// # Examples
///
/// ```rust
/// let attr = hamlet::attr::Attribute::new("id", "foo");
/// assert_eq!(format!("{}", attr), "id=\"foo\"");
/// ```
///
/// ```rust
/// let attr = hamlet::attr::Attribute::new("id", "bar & baz");
/// assert_eq!(format!("{}", attr), "id=\"bar & baz\"");
/// ```
///
/// ```rust
/// let attr = hamlet::attr::Attribute::new("invalid=id", "foo");
/// assert_eq!(format!("{}", attr), "invalid=id=\"foo\"");
/// ```
///
/// ```rust
/// let attr = hamlet::attr::Attribute::new("checked", "");
/// assert_eq!(format!("{}", attr), "checked");
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Attribute<'a> {
/// The [attribute's name][name]. The value of this field will not be
/// validated, you must ensure it meets the requirements specified in the
/// spec yourself.
///
/// [name]: https://www.w3.org/TR/html/syntax.html#syntax-attribute-name
///
/// # Examples
///
/// ```rust
/// let attr = hamlet::attr::Attribute::new("invalid=id", "foo");
/// assert_eq!(format!("{}", attr), "invalid=id=\"foo\"");
/// ```
pub name: Cow<'a, str>,

/// The [attribute's value][value]. This field will be escaped
/// automatically, if it is an empty string then the attribute will be
/// written with the 'Empty attribute syntax'.
///
/// [value]: https://www.w3.org/TR/html/syntax.html#syntax-attribute-value
///
/// # Examples
///
/// ```rust
/// let attr = hamlet::attr::Attribute::new("id", "bar & baz");
/// assert_eq!(format!("{}", attr), "id=\"bar &amp; baz\"");
/// ```
///
/// ```rust
/// let attr = hamlet::attr::Attribute::new("checked", "");
/// assert_eq!(format!("{}", attr), "checked");
/// ```
pub value: Cow<'a, str>,
}

Expand Down Expand Up @@ -85,11 +93,11 @@ impl<'a> fmt::Display for Attribute<'a> {
}
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq)]
/// A list of [`Attribute`](./struct.Attribute.html)s.
///
/// This is stored as a plain list instead of a set as in most cases it will
/// be a small collection over which linear search will be more efficient.
/// be a small collection over which a linear search will be more efficient.
pub struct AttributeList<'a>(Cow<'a, [Attribute<'a>]>);

impl<'a> AttributeList<'a> {
Expand All @@ -106,6 +114,26 @@ impl<'a> AttributeList<'a> {
AttributeList(Cow::Owned(attrs))
}

/// Pull all attributes out of this collection, useful if you need to
/// perform some more extensive modification.
///
/// ```rust
/// # #[macro_use] extern crate hamlet;
/// # fn main() {
/// use hamlet::attr::{ Attribute, AttributeList };
/// let attrs = attrs!(dataBar = "bar", dataBaz = "baz");
///
/// // Namespace all data attributes for some reason.
/// let attrs = AttributeList::from_vec(
/// attrs.into_vec().into_iter()
/// .map(|Attribute { name, value }| {
/// Attribute::new(name.replace("data-", "data-foo-"), value)
/// })
/// .collect());
///
/// assert_eq!(attrs.get("data-foo-bar"), Some("bar"));
/// # }
/// ```
pub fn into_vec(self) -> Vec<Attribute<'a>> {
self.0.into_owned()
}
Expand Down Expand Up @@ -136,8 +164,8 @@ impl<'a> AttributeList<'a> {
}

/// Unconditionally set an attribute to a value. If the attribute already
/// exists in the set will update its value, otherwise will add a new
/// attribute to the set.
/// exists in the list, update its value, otherwise add a new attribute to
/// the list.
///
/// # Examples
///
Expand Down Expand Up @@ -176,7 +204,7 @@ impl<'a> AttributeList<'a> {
}
}

/// Removes and returns the attribute it if there was one.
/// Removes and returns the attribute if there was one.
///
/// # Examples
///
Expand All @@ -200,6 +228,7 @@ impl<'a> AttributeList<'a> {
}
}

/// Returns an iterator over the list.
pub fn iter<'b>(&'b self) -> Iter<'b, 'a> {
Iter {
inner: self.0.as_ref(),
Expand All @@ -208,6 +237,7 @@ impl<'a> AttributeList<'a> {
}
}

/// Immutable [`AttributeList`](./struct.AttributeList.html) iterator.
pub struct Iter<'b, 'a: 'b> {
inner: &'b [Attribute<'a>],
index: usize,
Expand All @@ -221,3 +251,13 @@ impl<'a, 'b> Iterator for Iter<'b, 'a> {
self.inner.get(self.index - 1)
}
}

impl<'a, 'b> PartialEq<AttributeList<'b>> for AttributeList<'a> {
fn eq(&self, other: &AttributeList<'b>) -> bool {
let mut left = self.0.iter().collect::<Vec<_>>();
let mut right = other.0.iter().collect::<Vec<_>>();
left.sort();
right.sort();
left == right
}
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,16 @@
//! }
//! ```
#![warn(missing_docs)]

/// Currently contains just a semi-private utility function to support the
/// [`attrs!`](./macro.attrs!.html) macro.
pub mod util;

#[macro_use]
mod macros;

/// Contains structs for defining attributes on elements.
pub mod attr;
mod escape;
mod token;
Expand Down
128 changes: 118 additions & 10 deletions src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,81 @@ use std::borrow::Cow;
use attr::AttributeList;
use escape::Escaped;

#[derive(Clone, Debug)]
/// An HTML token. By convention, `Token::Text` should be preferred over
/// `Token::RawText` when a piece of text can be represented by both. For
/// instance, use `Token::Text` when tokenizing whitespaces or line-breaks, but
/// use `Token::RawText` for representing all text inside `<style>` tag.
#[derive(Clone, Debug, PartialEq, Eq)]
/// An HTML token, these are representations of everything needed to generate
/// an [HTML document](https://www.w3.org/TR/html/syntax.html#writing).
///
/// By convention, [`Token::Text`](#variant.Text) should be preferred over
/// [`Token::RawText`](#variant.RawText) when a piece of text can be
/// represented by both. For instance, use `Text` when tokenizing whitespaces
/// or line-breaks, but use `RawText` for representing all text inside
/// a `<style>` tag.
///
/// When `Display`ing a `Token`, the output stream is assumed to be Unicode, and
/// therefore only five characters are escaped: `&`, `<`, `>`, `"`, and `'`
/// ([ref](http://stackoverflow.com/a/7382028)).
pub enum Token<'a> {
/// A [start tag](https://www.w3.org/TR/html/syntax.html#syntax-start-tag)
/// token.
StartTag {
/// The element's [tag
/// name](https://www.w3.org/TR/html/syntax.html#syntax-tag-name).
name: Cow<'a, str>,

/// Any attributes for the start tag.
attrs: AttributeList<'a>,
/// Marker indicating self-closing tags such as `<br />`

/// Marker indicating the tag should be self-closing, such as `<br />`
/// (although `br` is a [void
/// element](https://www.w3.org/TR/html/syntax.html#void-elements) so
/// this has no effect on it).
self_closing: bool,
},

/// An [end tag](https://www.w3.org/TR/html/syntax.html#syntax-end-tag)
/// token.
EndTag {
/// The element's [tag
/// name](https://www.w3.org/TR/html/syntax.html#syntax-tag-name).
name: Cow<'a, str>,
},

/// The text contained will be escaped on `Display`.
Text(Cow<'a, str>),

/// The text contained will be `Display`ed as-is.
RawText(Cow<'a, str>),

/// Comments contained within `<!--` and `-->`. No validation is done to
/// ensure that the text does not contain `-->`.
/// ensure that the text conforms to the [html comment
/// syntax](https://www.w3.org/TR/html/syntax.html#syntax-comments).
Comment(Cow<'a, str>),
/// The HTML5 DOCTYPE declaration

/// The [HTML5 DOCTYPE
/// declaration](https://www.w3.org/TR/html/syntax.html#syntax-doctype)
/// (`<!DOCTYPE html>`)
DOCTYPE,
}

impl<'a> Token<'a> {
/// Create a [`StartTag`](#variant.StartTag) token with specified element
/// name and attributes, use [`closed()`](#method.closed) to set the
/// [`self_closing`](#variant.StartTag.field.self_closing) flag.
///
/// # Examples
///
/// ```rust
/// # #[macro_use] extern crate hamlet;
/// # fn main() {
/// assert_eq!(
/// hamlet::Token::start_tag("script", attrs!()),
/// hamlet::Token::StartTag {
/// name: std::borrow::Cow::Borrowed("script"),
/// attrs: attrs!(),
/// self_closing: false,
/// });
/// # }
/// ```
pub fn start_tag<S>(name: S, attrs: AttributeList<'a>) -> Token<'a>
where S: Into<Cow<'a, str>>
{
Expand All @@ -45,32 +89,96 @@ impl<'a> Token<'a> {
}
}

/// Create an [`EndTag`](#variant.EndTag) token with specified element
/// name.
///
/// # Examples
///
/// ```rust
/// assert_eq!(
/// hamlet::Token::end_tag("script"),
/// hamlet::Token::EndTag {
/// name: std::borrow::Cow::Borrowed("script"),
/// });
/// ```
pub fn end_tag<S>(name: S) -> Token<'a>
where S: Into<Cow<'a, str>>
{
Token::EndTag { name: name.into() }
}

/// Create a [`Text`](#variant.Text) token with specified text content.
///
/// # Examples
///
/// ```rust
/// assert_eq!(
/// hamlet::Token::text("hello world"),
/// hamlet::Token::Text(std::borrow::Cow::Borrowed("hello world")));
/// ```
pub fn text<S>(s: S) -> Token<'a>
where S: Into<Cow<'a, str>>
{
Token::Text(s.into())
}


/// Create a [`RawText`](#variant.RawText) token with specified raw text
/// content.
///
/// # Examples
///
/// ```rust
/// assert_eq!(
/// hamlet::Token::raw_text("hello world"),
/// hamlet::Token::RawText(std::borrow::Cow::Borrowed("hello world")));
/// ```
pub fn raw_text<S>(s: S) -> Token<'a>
where S: Into<Cow<'a, str>>
{
Token::RawText(s.into())
}

/// Create a [`Comment`](#variant.Comment) token with specified comment
/// content.
///
/// # Examples
///
/// ```rust
/// assert_eq!(
/// hamlet::Token::comment("hello world"),
/// hamlet::Token::Comment(std::borrow::Cow::Borrowed("hello world")));
/// ```
pub fn comment<S>(s: S) -> Token<'a>
where S: Into<Cow<'a, str>>
{
Token::Comment(s.into())
}

/// If `self` is a `StartTag`, returns the `Token` after setting
/// `self_closing` to `true`; otherwise, it is a no-op.
/// If `self` is a [`StartTag`](#variant.StartTag), returns a copy with
/// [`self_closing`](#variant.StartTag.field.self_closing) set to `true`;
/// otherwise, returns `self`.
///
/// # Examples
///
/// ```rust
/// # #[macro_use] extern crate hamlet;
/// # fn main() {
/// assert_eq!(
/// hamlet::Token::start_tag("br", attrs!()).closed(),
/// hamlet::Token::StartTag {
/// name: std::borrow::Cow::Borrowed("br"),
/// attrs: attrs!(),
/// self_closing: true,
/// });
/// # }
/// ```
///
/// ```rust
/// assert_eq!(
/// hamlet::Token::text("hello world").closed(),
/// hamlet::Token::Text(std::borrow::Cow::Borrowed("hello world")));
/// ```
pub fn closed(self) -> Token<'a> {
if let Token::StartTag { name, attrs, .. } = self {
Token::StartTag {
Expand Down

0 comments on commit 49de63f

Please sign in to comment.