You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yuurei/src/html/mod.rs

166 lines
3.4 KiB

//! HTML rendering
use super::*;
use std::{
fmt::{
self,
Display,
},
io,
};
pub struct HtmlTokenStream<W: io::Write>(W);
impl<W: io::Write> HtmlTokenStream<W>
{
fn push<T: DisplayHtml>(&mut self, token: T) -> io::Result<()>
{
write!(&mut self.0, "{}", display_html(&token)) //TODO: How to do async version of this?
}
}
/// Coerce a `DisplayHtml` value into an opaque implementor of `fmt::Display`.
#[inline] pub fn display_html<'a, T: DisplayHtml+?Sized>(from: &'a T) -> impl fmt::Display +'a
{
struct Wrap<'a, T: DisplayHtml+?Sized>(&'a T);
impl<'a,T> fmt::Display for Wrap<'a,T>
where T: DisplayHtml+?Sized
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
DisplayHtml::fmt(self.0, f)
}
}
Wrap(from)
}
/// Coerse a `Display` value into a `DisplayHtml` by HTML-escaping its output.
///
/// To output raw HTML, see `Safe<T>`.
#[inline] pub fn escape_html<'a, T: Display+?Sized>(from: &'a T) -> impl DisplayHtml +'a
{
struct Wrap<'a, T: Display+?Sized>(&'a T);
struct FilterStream<T,F>(T, F)
where F: FnMut(&mut T, char) -> fmt::Result;
impl<T,F> fmt::Write for FilterStream<T,F>
where T: fmt::Write,
F: FnMut(&mut T, char) -> fmt::Result
{
fn write_str(&mut self, s: &str) -> fmt::Result
{
for c in s.chars()
{
self.1(&mut self.0, c)?;
}
Ok(())
}
fn write_char(&mut self, c: char) -> fmt::Result
{
self.1(&mut self.0, c)
}
}
impl<'a,T> DisplayHtml for Wrap<'a,T>
where T: Display +?Sized
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
use smallmap::Map;
lazy_static! {
static ref HTML_EQ_TABLE: Map<char, &'static str> = {
let mut map = Map::new();
map.insert('&', "&amp;");
map.insert('<', "&lt;");
map.insert('>', "&gt;");
map.insert('"', "&quot;");
map
};
}
let mut output = FilterStream(f, |output, c| {
output.write_str(match HTML_EQ_TABLE.get(&c) {
Some(s) => s,
None => {
return output.write_char(c);
},
})
});
use fmt::Write;
write!(&mut output, "{}", self.0)
}
}
Wrap(from)
}
/// HTML-safe wrapper around
#[derive(Debug, PartialEq, Eq)]
pub struct Safe<T: Display>(T);
impl<T: Display> Safe<T>
{
/// Create a new instance with this value
#[inline] pub fn new(value: T) -> Self
{
Self(value)
}
/// Consume this instance into its inner value
pub fn into_inner(self) -> T
{
self.0
}
/// Gets a reference to the inner value
pub fn display(&self) -> &T
{
&self.0
}
}
impl<T: Display> From<T> for Safe<T>
{
#[inline] fn from(from: T) -> Self
{
Self(from)
}
}
/// A trait for displaying as html.
///
/// Any type implementing `Display` will implement `DisplayHtml` by HTML-escaping the output from it's display formatter.
/// You can also use `Safe<T>` to render as raw HTML.
pub trait DisplayHtml
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result;
#[inline] fn to_html(&self) -> String
{
display_html(self).to_string()
}
}
impl<T: Display> DisplayHtml for Safe<T>
{
#[inline] fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result
{
self.0.fmt(fmt)
}
#[inline] fn to_html(&self) -> String
{
self.0.to_string()
}
}
impl<T: ?Sized> DisplayHtml for T
where T: Display
{
#[inline] fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result
{
escape_html(self).fmt(fmt)
}
}