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
#![deny(missing_docs)]

//! escapade - type assisted html safety
//!
//!`escapade` is inspired by ActiveSupports SafeBuffer.
//!
//! `escapade` provides String concatenation and writing, but automatically escapes any HTML in the data in the process. This prevents accidental unescaped writes to the output.
//!
//! The library provides both a String type for HTML-safe concatenation and a writer, wrapping types implementing `Write`.
//!
//! The library works with any type that implements `AsRef<str>`.
mod encode;
mod entities;

use encode::encode_attribute;
use encode::encode_attribute_w;
use std::io::Write;
use std::io;

/// An escaped string-like value
///
/// Escaped wraps a value with the bounds `AsRef<str>`.
/// It can work on any of those values, but any operations
/// on them will return `Escaped<String>` and thus allocate.
pub struct Escaped<T: AsRef<str>> {
    inner: T
}

impl<T: AsRef<str>> Escaped<T> {
    /// Consumes the escaped marker and returns
    /// the wrapped value.
    pub fn into_inner(self) -> T {
        self.inner
    }
}

/// Trait marking a value as appendable to `Escaped`
///
/// Values marked as `Append` can be appended to `Escaped`.
pub trait Append<T> {
    /// Append any string-like value
    fn append_str(&mut self, string: T);
}

impl<T: Escapable> Append<T> for Escaped<String> {
    fn append_str(&mut self, string: T) {
        self.append_str(string.escape())
    }
}

impl<T: AsRef<str>> Append<Escaped<T>> for Escaped<String> {
    fn append_str(&mut self, string: Escaped<T>) {
        self.inner.push_str(string.inner.as_ref());
    }
}

impl<T: AsRef<str>> Escaped<T> {
    fn as_ref(&self) -> &str {
        self.inner.as_ref()
    }
}

/// A wrapper for writer automatically escaping text written to it
pub struct EscapedWriter<T: Write> {
    inner: T
}

impl<T: Write> EscapedWriter<T> {
    /// Create a new `EscapedWriter`
    pub fn new(inner: T) -> Self {
        EscapedWriter { inner: inner }
    }

    /// Consume the writer, returning the wrapped value
    pub fn into_inner(self) -> T {
        self.inner
    }
}

/// Marks values as escapable
///
/// `Escapable` values can either be escaped by hand
/// or by appending them to either a `SafeWriter` or
/// an `Escaped` value.
///
/// `Escapable` values can also be considered safe
/// by calling the appropriate value. Safe values
/// are exempted from further escaping.
pub trait Escapable: AsRef<str> {
    /// Escape the value at hand and return
    /// an escaped String.
    fn escape(&self) -> Escaped<String>;
    /// Mark the value as safe, exempting it from
    /// further escaping.
    fn safe(&self) -> Escaped<String>;
}

impl<T: AsRef<str>> Escapable for T {
    fn escape(&self) -> Escaped<String> {
        Escaped { inner: encode_attribute(self.as_ref()) }
    }

    fn safe(&self) -> Escaped<String> {
        Escaped { inner: self.as_ref().into() }
    }
}

/// Escaped writing to buffers
///
/// This trait handles writing of different kinds of values
/// to an `EscapedWriter`. It is intended to be implemented
/// for `EscapedWriter` for each kind of value that is allowed
/// to be written to.
///
/// The implementor must properly escape the passed value before
/// writing it.
pub trait EscapedWrite<T> {
    /// Write the passed string-like value to the writer,
    /// returning the writers Result.
    fn write_str(&mut self, value: T) -> io::Result<()>;
}

impl<X: AsRef<str>, W: Write> EscapedWrite<Escaped<X>> for EscapedWriter<W> {
    fn write_str(&mut self, value: Escaped<X>) -> io::Result<()> {
        self.inner.write_all(value.as_ref().as_bytes())
    }
}

impl<'a, X: Escapable, W: Write> EscapedWrite<X> for EscapedWriter<W> {
    fn write_str(&mut self, value: X) -> io::Result<()> {
        encode_attribute_w(value.as_ref(), &mut self.inner)
    }
}