1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg_attr(docsrs, doc(cfg(feature = "const-format")))]
#[cfg(feature = "const-format")]
pub use fhtml_macros::formatcp;
pub use fhtml_macros::write;
#[doc(hidden)]
pub mod _internal {
#[cfg(feature = "const-format")]
pub use ::const_format::*;
}
/// Writes formatted HTML to a `String`.
///
/// Similar to [`std::format!`], this macro returns an owned `String` with the
/// formatted content.
///
/// The syntax and overall behaviour is identical to [`fhtml::write!`], where
/// more detailed documentation can be found.
///
/// [`std::format!`]: std::format
/// [`fhtml::write!`]: crate::write
///
/// # Examples
///
/// ## Simple usage
///
/// ```rust
/// fhtml::format!(<div>"Hello, World!"</div>); // "<div>Hello, World!</div>"
/// ```
#[macro_export]
macro_rules! format {
($($t:tt)*) => {{
use ::std::fmt::Write;
let mut output = String::new();
let _ = $crate::write!(output, $($t)*);
output
}};
}
/// Escapes special HTML characters in a string.
///
/// This function takes an input string and replaces certain characters with
/// their corresponding HTML escape sequences. The following characters are
/// escaped:
///
/// - `&` becomes `&`
/// - `<` becomes `<`
/// - `>` becomes `>`
/// - `"` becomes `"`
/// - `'` becomes `'`
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let raw = "Hello, <world> & 'friends'";
/// let escaped = fhtml::escape(raw);
/// assert_eq!(escaped, "Hello, <world> & 'friends'");
/// ```
///
/// This is useful for preventing HTML injection attacks when displaying
/// untrusted input in a web page.
///
/// # Parameters
///
/// - `input`: A value that can be converted to a string slice (`&str`). This
/// includes `String` and `&str`.
///
/// # Returns
///
/// A `String` with the special HTML characters replaced by their escape
/// sequences.
///
/// # Performance
///
/// The function preallocates a `String` with the same length as the input,
/// which could save some reallocation costs if the input contains few
/// characters that need to be escaped.
#[inline]
pub fn escape<T: AsRef<str>>(input: T) -> String {
let input = input.as_ref();
let mut escaped = String::with_capacity(input.len());
for c in input.chars() {
match c {
'&' => escaped.push_str("&"),
'<' => escaped.push_str("<"),
'>' => escaped.push_str(">"),
'"' => escaped.push_str("""),
'\'' => escaped.push_str("'"),
_ => escaped.push(c),
}
}
escaped
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn escape_ampersand() {
assert_eq!(escape("&"), "&");
assert_eq!(escape("This & that"), "This & that");
}
#[test]
fn escape_less_than() {
assert_eq!(escape("<"), "<");
assert_eq!(escape("a < b"), "a < b");
}
#[test]
fn escape_greater_than() {
assert_eq!(escape(">"), ">");
assert_eq!(escape("a > b"), "a > b");
}
#[test]
fn escape_double_quote() {
assert_eq!(escape("\""), """);
assert_eq!(escape("She said \"hello\""), "She said "hello"");
}
#[test]
fn escape_single_quote() {
assert_eq!(escape("'"), "'");
assert_eq!(escape("It's a test"), "It's a test");
}
#[test]
fn escape_multiple_special_chars() {
assert_eq!(
escape("<div> & \"text\""),
"<div> & "text""
);
assert_eq!(escape("5 > 3 & 2 < 4"), "5 > 3 & 2 < 4");
}
#[test]
fn escape_no_special_chars() {
assert_eq!(escape("Hello, world!"), "Hello, world!");
assert_eq!(escape("Rust programming"), "Rust programming");
}
#[test]
fn escape_empty_string() {
assert_eq!(escape(""), "");
}
#[test]
fn escape_mixed_input() {
assert_eq!(
escape("The price is 5 > 4 & 3 < 6"),
"The price is 5 > 4 & 3 < 6"
);
assert_eq!(
escape("Use 'single' and \"double\" quotes"),
"Use 'single' and "double" quotes"
);
}
}