use crate::Renderer;
use crate::renderers::escape_html;
use std::borrow::Cow;
use std::fmt::Display;
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Trusted(String);
impl Trusted {
#[must_use]
pub const fn new() -> Self {
Trusted(String::new())
}
#[must_use]
pub const fn from_markup(markup: String) -> Self {
Trusted(markup)
}
#[must_use]
pub fn escaping(value: &dyn Display) -> Self {
let mut out = Trusted::new();
out.push_escaped(value);
out
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn into_string(self) -> String {
self.0
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
pub fn push(&mut self, content: &impl Content) {
content.write_content(self);
}
pub fn push_markup(&mut self, markup: &str) {
self.0.push_str(markup);
}
pub fn push_escaped(&mut self, value: &dyn Display) {
escape_html(&value.to_string(), &mut self.0);
}
}
impl AsRef<str> for Trusted {
fn as_ref(&self) -> &str {
&self.0
}
}
impl From<Trusted> for String {
fn from(trusted: Trusted) -> String {
trusted.0
}
}
impl crate::Render for Trusted {
fn render_into(&self, r: &mut dyn Renderer) {
r.write_raw(&self.0);
}
}
pub struct Sink<'a>(&'a mut Trusted);
impl Renderer for Sink<'_> {
fn write_raw(&mut self, s: &str) {
self.0.push_markup(s);
}
fn write_escaped(&mut self, value: &dyn Display) {
self.0.push_escaped(value);
}
fn finish(self: Box<Self>) -> String {
String::new()
}
}
impl Trusted {
#[must_use]
pub fn sink(&mut self) -> Sink<'_> {
Sink(self)
}
}
pub trait ToTrusted {
fn to_trusted(&self) -> Trusted;
}
impl ToTrusted for str {
fn to_trusted(&self) -> Trusted {
Trusted(self.to_owned())
}
}
impl ToTrusted for String {
fn to_trusted(&self) -> Trusted {
Trusted(self.clone())
}
}
impl ToTrusted for Cow<'_, str> {
fn to_trusted(&self) -> Trusted {
Trusted(self.as_ref().to_owned())
}
}
impl<T: ToTrusted + ?Sized> ToTrusted for &T {
fn to_trusted(&self) -> Trusted {
(**self).to_trusted()
}
}
pub trait Content {
fn write_content(&self, out: &mut Trusted);
}
impl Content for Trusted {
fn write_content(&self, out: &mut Trusted) {
out.push_markup(&self.0);
}
}
impl Content for str {
fn write_content(&self, out: &mut Trusted) {
escape_html(self, &mut out.0);
}
}
impl Content for String {
fn write_content(&self, out: &mut Trusted) {
escape_html(self, &mut out.0);
}
}
impl Content for Cow<'_, str> {
fn write_content(&self, out: &mut Trusted) {
escape_html(self, &mut out.0);
}
}
macro_rules! content_via_display {
($($ty:ty),* $(,)?) => {
$(
impl Content for $ty {
fn write_content(&self, out: &mut Trusted) {
out.push_escaped(self);
}
}
)*
};
}
content_via_display!(
bool, char, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64,
);
impl<T: Content> Content for Option<T> {
fn write_content(&self, out: &mut Trusted) {
if let Some(content) = self {
content.write_content(out);
}
}
}
impl<T: Content + ?Sized> Content for &T {
fn write_content(&self, out: &mut Trusted) {
(**self).write_content(out);
}
}
impl<T: Content> Content for [T] {
fn write_content(&self, out: &mut Trusted) {
for item in self {
item.write_content(out);
}
}
}
impl<T: Content, const N: usize> Content for [T; N] {
fn write_content(&self, out: &mut Trusted) {
for item in self {
item.write_content(out);
}
}
}
impl<T: Content> Content for Vec<T> {
fn write_content(&self, out: &mut Trusted) {
for item in self {
item.write_content(out);
}
}
}
macro_rules! content_for_tuples {
($(($($name:ident),+),)*) => {
$(
#[allow(non_snake_case)]
impl<$($name: Content),+> Content for ($($name,)+) {
fn write_content(&self, out: &mut Trusted) {
let ($($name,)+) = self;
$($name.write_content(out);)+
}
}
)*
};
}
content_for_tuples!(
(A),
(A, B),
(A, B, C),
(A, B, C, D),
(A, B, C, D, E),
(A, B, C, D, E, F),
(A, B, C, D, E, F, G),
(A, B, C, D, E, F, G, H),
(A, B, C, D, E, F, G, H, I),
(A, B, C, D, E, F, G, H, I, J),
(A, B, C, D, E, F, G, H, I, J, K),
(A, B, C, D, E, F, G, H, I, J, K, L),
);
pub trait Value {
fn write_value(&self, r: &mut dyn Renderer);
}
impl<T: Display + ?Sized> Value for T {
fn write_value(&self, r: &mut dyn Renderer) {
r.write_escaped(&self);
}
}
macro_rules! trusted_value {
($($ty:ty),* $(,)?) => {
$(
impl Value for $ty {
fn write_value(&self, r: &mut dyn Renderer) {
r.write_raw(self.as_str());
}
}
)*
};
}
trusted_value!(Trusted, &Trusted);
#[inline]
pub fn splice<T: Value + ?Sized>(value: &T, r: &mut dyn Renderer) {
value.write_value(r);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::HtmlRenderer;
fn spliced<T: Value + ?Sized>(value: &T) -> String {
let mut r = HtmlRenderer::new();
splice(value, &mut r);
r.as_str().to_owned()
}
#[test]
fn markup_is_spliced_whole() {
assert_eq!(
spliced(&Trusted::from_markup("<b>x</b>".into())),
"<b>x</b>"
);
}
#[test]
fn a_reference_to_markup_is_still_markup() {
let markup = Trusted::from_markup("<i>x</i>".into());
assert_eq!(spliced(&&markup), "<i>x</i>");
}
#[test]
fn escaping_is_the_inverse_of_a_claim() {
assert_eq!(Trusted::escaping(&"<b>").as_str(), "<b>");
assert_eq!("<b>".to_trusted().as_str(), "<b>");
}
#[test]
fn pushing_escapes_data_and_keeps_markup() {
let mut out = Trusted::new();
out.push(&"a<b");
out.push(&Trusted::from_markup("<br>".into()));
out.push(&Some("c&d"));
out.push(&None::<&str>);
assert_eq!(out.as_str(), "a<b<br>c&d");
}
#[test]
fn a_tuple_writes_its_parts_in_order() {
let mut out = Trusted::new();
out.push(&(Trusted::from_markup("<b>".into()), "x<", 1_u8));
assert_eq!(out.as_str(), "<b>x<1");
}
#[test]
fn a_list_writes_every_item() {
let mut out = Trusted::new();
out.push(&vec!["a", "<b>"]);
assert_eq!(out.as_str(), "a<b>");
}
#[test]
fn markup_is_read_back_as_a_string_explicitly() {
let markup = Trusted::from_markup("<b>x</b>".into());
assert_eq!(markup.as_str(), "<b>x</b>");
assert_eq!(markup.into_string(), "<b>x</b>");
}
}