use core::fmt;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "std")]
use std::io;
use crate::error::{self, Error};
use crate::render::RenderOnce;
pub trait Template: RenderOnce + Sized {
#[cfg(feature = "alloc")]
fn into_string(self) -> Result<String, Error> {
let mut string = String::with_capacity(self.size_hint());
self.write_to_string(&mut string)?;
string.shrink_to_fit();
Ok(string)
}
#[cfg(feature = "alloc")]
fn write_to_string(self, string: &mut String) -> Result<(), Error> {
let mut buffer = TemplateBuffer {
writer: InnerTemplateWriter::Str(string),
error: Default::default(),
};
self.render_once(&mut buffer);
buffer.into_result()
}
fn write_to_fmt(self, writer: &mut dyn fmt::Write) -> Result<(), Error> {
let mut buffer = TemplateBuffer {
writer: InnerTemplateWriter::Fmt(writer),
error: Default::default(),
};
self.render_once(&mut buffer);
buffer.into_result()
}
#[cfg(feature = "std")]
fn write_to_io(self, writer: &mut dyn io::Write) -> Result<(), Error> {
let mut buffer = TemplateBuffer {
writer: InnerTemplateWriter::Io(writer),
error: Default::default(),
};
self.render_once(&mut buffer);
buffer.into_result()
}
}
impl<T: RenderOnce + Sized> Template for T {}
pub struct TemplateBuffer<'a> {
writer: InnerTemplateWriter<'a>,
error: Error,
}
enum InnerTemplateWriter<'a> {
Fmt(&'a mut dyn fmt::Write),
#[cfg(feature = "alloc")]
Str(&'a mut String),
#[cfg(feature = "std")]
Io(&'a mut dyn io::Write),
}
impl<'a> TemplateBuffer<'a> {
#[cold]
#[cfg(feature = "std")]
pub fn record_error<E: Into<Box<dyn std::error::Error + Send + Sync>>>(&mut self, e: E) {
self.error.render.push(e.into());
}
#[cold]
#[cfg(all(not(feature = "std"), feature = "alloc"))]
pub fn record_error<E: alloc::string::ToString>(&mut self, e: E) {
self.error.render.push(e.to_string());
}
#[cold]
#[cfg(not(feature = "alloc"))]
pub fn record_error(&mut self, e: &'static str) {
if self.error.render.is_none() {
self.error.render = Some(e);
}
}
#[inline(always)]
pub fn write_raw(&mut self, text: &str) {
use alloc::fmt::Write;
let _ = self.as_raw_writer().write_str(text);
}
#[inline]
pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) {
use alloc::fmt::Write;
let _ = self.as_writer().write_fmt(args);
}
#[inline]
pub fn write_str(&mut self, text: &str) {
use alloc::fmt::Write;
let _ = self.as_writer().write_str(text);
}
#[inline]
pub fn as_writer<'b>(&'b mut self) -> TemplateWriter<'a, 'b> {
TemplateWriter(self)
}
#[inline]
pub fn as_raw_writer<'b>(&'b mut self) -> RawTemplateWriter<'a, 'b> {
RawTemplateWriter(self)
}
fn into_result(self) -> Result<(), Error> {
if error::is_empty(&self.error) {
Ok(())
} else {
Err(self.error)
}
}
}
#[cfg(not(feature = "std"))]
#[inline(always)]
fn new_fmt_err() -> fmt::Error {
fmt::Error
}
#[cfg(feature = "std")]
#[inline(always)]
fn new_fmt_err() -> io::Error {
io::Error::new(io::ErrorKind::Other, "Format Error")
}
pub struct RawTemplateWriter<'a, 'b>(&'b mut TemplateBuffer<'a>);
impl<'a, 'b> fmt::Write for RawTemplateWriter<'a, 'b> {
#[inline(always)]
fn write_str(&mut self, text: &str) -> fmt::Result {
use self::InnerTemplateWriter::*;
if !error::is_empty(&self.0.error) {
return Ok(());
}
match self.0.writer {
Fmt(ref mut writer) => {
if writer.write_str(text).is_err() {
self.0.error.write = Some(new_fmt_err());
}
}
#[cfg(feature = "alloc")]
Str(ref mut writer) => {
let _ = writer.write_str(text);
}
#[cfg(feature = "std")]
Io(ref mut writer) => {
self.0.error.write = writer.write_all(text.as_bytes()).err();
}
}
Ok(())
}
}
pub struct TemplateWriter<'a, 'b>(&'b mut TemplateBuffer<'a>);
impl<'a, 'b> fmt::Write for TemplateWriter<'a, 'b> {
fn write_str(&mut self, text: &str) -> fmt::Result {
use self::InnerTemplateWriter::*;
if !error::is_empty(&self.0.error) {
return Ok(());
}
fn should_escape(b: u8) -> bool {
(b | 0x4) == b'&' || (b | 0x2) == b'>'
}
match self.0.writer {
Fmt(ref mut writer) => {
for c in text.chars() {
if (match (c.is_ascii() && should_escape(c as u8), c as u8) {
(true, b'&') => writer.write_str("&"),
(true, b'"') => writer.write_str("""),
(true, b'<') => writer.write_str("<"),
(true, b'>') => writer.write_str(">"),
_ => writer.write_char(c),
})
.is_err()
{
self.0.error.write = Some(new_fmt_err());
break;
}
}
}
#[cfg(feature = "alloc")]
Str(ref mut writer) => {
for b in text.bytes() {
match (should_escape(b), b) {
(true, b'&') => writer.push_str("&"),
(true, b'"') => writer.push_str("""),
(true, b'<') => writer.push_str("<"),
(true, b'>') => writer.push_str(">"),
_ => unsafe { writer.as_mut_vec() }.push(b),
}
}
}
#[cfg(feature = "std")]
Io(ref mut writer) => {
for b in text.bytes() {
if let Err(e) = match (should_escape(b), b) {
(true, b'&') => writer.write_all(b"&"),
(true, b'"') => writer.write_all(b"""),
(true, b'<') => writer.write_all(b"<"),
(true, b'>') => writer.write_all(b">"),
_ => writer.write_all(&[b] as &[u8]),
} {
self.0.error.write = Some(e);
break;
}
}
}
}
Ok(())
}
}