feat: serve rendered pages over http, spartan and nex

This commit is contained in:
randogoth 2026-10-04 19:55:21 +03:00
parent 46f12b10da
commit 2f38b8b929
30 changed files with 2896 additions and 90 deletions

48
wap/src/escape.rs Normal file
View file

@ -0,0 +1,48 @@
//! XML escaping.
/// Escape text for an element's content.
pub fn text(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for ch in value.chars() {
match ch {
'&' => out.push_str("&"),
'<' => out.push_str("&lt;"),
'>' => out.push_str("&gt;"),
_ => out.push(ch),
}
}
out
}
/// Escape text for an attribute value, which is always double-quoted here.
pub fn attr(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for ch in value.chars() {
match ch {
'&' => out.push_str("&amp;"),
'<' => out.push_str("&lt;"),
'>' => out.push_str("&gt;"),
'"' => out.push_str("&quot;"),
_ => out.push(ch),
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn escapes_markup_characters() {
assert_eq!(text("a & b < c > d"), "a &amp; b &lt; c &gt; d");
// A quote is only significant inside an attribute, so content keeps it.
assert_eq!(text("say \"hi\""), "say \"hi\"");
assert_eq!(attr("say \"hi\" & bye"), "say &quot;hi&quot; &amp; bye");
}
#[test]
fn leaves_ordinary_text_alone() {
assert_eq!(text("plain text 123"), "plain text 123");
}
}