A Rust port of Python's markupsafe: HTML escaping
with a string type that tracks whether its contents are already safe. The escaping matches the
reference implementation byte-for-byte (verified by differential testing).
In Python, Markup is a str subclass that enforces "this text is safe" by convention. Here
it's a newtype, so safety is a compile-time guarantee: combining a Markup with raw text
forces the raw text to be escaped, making it impossible to accidentally treat unescaped input as safe.
escape: replace&,<,>,',"with HTML-safe entities, returning aMarkup.Markup: a string known to be safe (escaped or explicitly marked). Combining it with a plain&strescapes the&str; combining twoMarkups does not.escape_silent(treatsNoneas empty),escape_html(for types implementingHasHtml), and theHasHtmltrait (the__html__protocol).
cargo add markupsafe-rs[dependencies]
markupsafe-rs = "0.1"Requires a Rust toolchain with 2024-edition support (Rust 1.85 or newer).
use markupsafe_rs::{escape, Markup};
// Escape untrusted text.
let safe = escape("<script>alert('x')</script>");
assert_eq!(safe.as_str(), "<script>alert('x')</script>");
// Mark trusted text safe without escaping it.
let trusted = Markup::new("<em>Hello</em>");
assert_eq!(trusted.as_str(), "<em>Hello</em>");
// Concatenation escapes only the unsafe side.
let combined = Markup::new("<em>Hello</em> ") + "<foo>";
assert_eq!(combined.as_str(), "<em>Hello</em> <foo>");
// Two safe pieces are joined as-is.
let both = Markup::new("<a>") + Markup::new("<b>");
assert_eq!(both.as_str(), "<a><b>");This crate covers markupsafe's core: HTML escaping and the type-safe Markup. Not ported:
Python's ~30 str-method overrides (Markup is a newtype here, not a str subclass, so it
neither has nor needs them), %/format formatting, and unescape/striptags (which need
full HTML-entity decoding; use a dedicated HTML-entities crate for that).
Licensed under the BSD 3-Clause License, matching the upstream markupsafe
project.