diff options
Diffstat (limited to 'askama_escape/src/lib.rs')
-rw-r--r-- | askama_escape/src/lib.rs | 56 |
1 files changed, 52 insertions, 4 deletions
diff --git a/askama_escape/src/lib.rs b/askama_escape/src/lib.rs index ad08e55..1788843 100644 --- a/askama_escape/src/lib.rs +++ b/askama_escape/src/lib.rs @@ -1,10 +1,7 @@ -#![no_std] +#![cfg_attr(not(any(feature = "json", test)), no_std)] #![deny(elided_lifetimes_in_paths)] #![deny(unreachable_pub)] -#[cfg(test)] -extern crate std; - use core::fmt::{self, Display, Formatter, Write}; use core::str; @@ -175,6 +172,57 @@ pub trait Escaper { const FLAG: u8 = b'>' - b'"'; +/// Escape chevrons, ampersand and apostrophes for use in JSON +#[cfg(feature = "json")] +#[derive(Debug, Clone, Default)] +pub struct JsonEscapeBuffer(Vec<u8>); + +#[cfg(feature = "json")] +impl JsonEscapeBuffer { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn finish(self) -> String { + unsafe { String::from_utf8_unchecked(self.0) } + } +} + +#[cfg(feature = "json")] +impl std::io::Write for JsonEscapeBuffer { + fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> { + macro_rules! push_esc_sequence { + ($start:ident, $i:ident, $self:ident, $bytes:ident, $quote:expr) => {{ + if $start < $i { + $self.0.extend_from_slice(&$bytes[$start..$i]); + } + $self.0.extend_from_slice($quote); + $start = $i + 1; + }}; + } + + self.0.reserve(bytes.len()); + let mut start = 0; + for (i, b) in bytes.iter().enumerate() { + match *b { + b'&' => push_esc_sequence!(start, i, self, bytes, br#"\u0026"#), + b'\'' => push_esc_sequence!(start, i, self, bytes, br#"\u0027"#), + b'<' => push_esc_sequence!(start, i, self, bytes, br#"\u003c"#), + b'>' => push_esc_sequence!(start, i, self, bytes, br#"\u003e"#), + _ => (), + } + } + if start < bytes.len() { + self.0.extend_from_slice(&bytes[start..]); + } + Ok(bytes.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; |