Crate toph

source ·
Expand description

An API for building HTML documents in Rust.

  • It’s Just Rust Code.
  • Safely set attributes and content on HTML elements.
  • Link css & javascript snippets to HTML elements, such that those snippets appear when the linked element is displayed.
    • CSS & JS assets are included in the final HTML output.

The crate also implements a set of core css components so you don’t have to

§Example

use toph::{attr, Node, tag::*};

let navigation = [("Home", "/about me"), ("Posts", "/posts")];
let mut doc = [
    doctype_,
    html_.with(attr![lang="en"])
        .set([
            head_.set([title_.set(["My Webpage"])]),
            body_.set([
                ul_.with(attr![id="navigation"])
                    .set(
                        navigation.into_iter().map(|(caption, url)| {
                            li_.set([a_.with(attr![href=url]).set([caption])])
                        }).collect::<Vec<_>>()
                    ),
                h1_.stylesheet("h1 { text-decoration: underline; }")
                    .set(["My Webpage"])
            ])
        ])
];

assert_eq!(
    Node::render_pretty(doc),
    r#"<!DOCTYPE html>
<html lang="en">
  <head>
    <title>
      My Webpage
    </title>
    <style>
      h1 { text-decoration: underline; }
    </style>
  </head>
  <body>
    <ul id="navigation">
      <li>
        <a href="/about%20me">
          Home
        </a>
      </li>
      <li>
        <a href="/posts">
          Posts
        </a>
      </li>
    </ul>
    <h1>
      My Webpage
    </h1>
  </body>
</html>
"#);

§XSS Prevention

A couple measures are taken to protect against Cross-site scripting attacks (XSS):

  • Strings are appropriately encoded in HTML, attribute and URL contexts:
use toph::{attr, tag::*, Node};

let xss_attr_attempt = r#"" onclick="alert(1)""#;
let xss_attempt = r#"<script>alert(1)"#;
let url = "/path with space";

let mut span = span_
    .with(attr![class=xss_attr_attempt])
    .set([xss_attempt]);

let mut anchor = a_
    .with(attr![href=url])
    .set(["A link"]);

assert_eq!(
    Node::render([span]),
    r#"<span class="&quot; onclick=&quot;alert(1)&quot;">&lt;script&gt;alert(1)</span>"#
);

assert_eq!(
    Node::render([anchor]),
    r#"<a href="/path%20with%20space">A link</a>"#
);
  • JavaScript & CSS snippets can only be set using literal (i.e. 'static) string slices.
    • It is frequently useful to parameterize styles though, so the var method is provided.
    • To include files, use include_str
use toph::{tag::*, Node};

let user_input = "1rem";
let css = format!("p {{ font-size: {}; }}", user_input);

// This does not compile
// let mut html = html_.set([ head_, p_.stylesheet(css)]);
// Neither does this
// let mut html = html_.set([ head_, p_.stylesheet(&css)]);

// Technically, you _could_ leak the string...
// Why you would want to leak memory for this purpose is beyond me.
// You are on your own.
// let mut html = html_.set([ head_, p_.stylesheet(css.leak())]);

// Set snippets using string literals
// Parameterize with css custom variables & `var()`
let css = "p { font-size: var(--font-size); }";
let mut html = html_.set([
    head_,
    p_.stylesheet(css).var("font-size", user_input),
]);
assert_eq!(
  Node::render_pretty([html]),
  r#"<html>
  <head>
    <style>
      p { font-size: var(--font-size); }
    </style>
  </head>
  <p style="--font-size: 1rem;">
  </p>
</html>
"#);

Modules§

  • Composable CSS Layout primitives and components
  • HTML Elements

Macros§

  • Attribute list builder

Structs§