#![deny(elided_lifetimes_in_paths)]
#![deny(unreachable_pub)]
mod error;
pub mod filters;
pub mod helpers;
use std::fmt;
pub use reva_derive::Template;
pub use reva_escape::{Html, MarkupDisplay, Text};
#[doc(hidden)]
pub use crate as shared;
pub use crate::error::{Error, Result};
pub trait Template: fmt::Display {
fn render(&self) -> Result<String> {
let mut buf = String::new();
let _ = buf.try_reserve(Self::SIZE_HINT);
self.render_into(&mut buf)?;
Ok(buf)
}
fn render_into(&self, writer: &mut (impl std::fmt::Write + ?Sized)) -> Result<()>;
#[inline]
fn write_into(&self, writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
writer.write_fmt(format_args!("{self}"))
}
const EXTENSION: Option<&'static str>;
const SIZE_HINT: usize;
const MIME_TYPE: &'static str;
}
impl<T: Template + ?Sized> Template for &T {
#[inline]
fn render_into(&self, writer: &mut (impl std::fmt::Write + ?Sized)) -> Result<()> {
T::render_into(self, writer)
}
#[inline]
fn render(&self) -> Result<String> {
T::render(self)
}
#[inline]
fn write_into(&self, writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
T::write_into(self, writer)
}
const EXTENSION: Option<&'static str> = T::EXTENSION;
const SIZE_HINT: usize = T::SIZE_HINT;
const MIME_TYPE: &'static str = T::MIME_TYPE;
}
pub trait DynTemplate {
fn dyn_render(&self) -> Result<String>;
fn dyn_render_into(&self, writer: &mut dyn std::fmt::Write) -> Result<()>;
fn dyn_write_into(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()>;
fn extension(&self) -> Option<&'static str>;
fn size_hint(&self) -> usize;
fn mime_type(&self) -> &'static str;
}
impl<T: Template> DynTemplate for T {
fn dyn_render(&self) -> Result<String> {
<Self as Template>::render(self)
}
fn dyn_render_into(&self, writer: &mut dyn std::fmt::Write) -> Result<()> {
<Self as Template>::render_into(self, writer)
}
#[inline]
fn dyn_write_into(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
writer.write_fmt(format_args!("{self}"))
}
fn extension(&self) -> Option<&'static str> {
Self::EXTENSION
}
fn size_hint(&self) -> usize {
Self::SIZE_HINT
}
fn mime_type(&self) -> &'static str {
Self::MIME_TYPE
}
}
impl fmt::Display for dyn DynTemplate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.dyn_render_into(f).map_err(|_| ::std::fmt::Error {})
}
}
#[cfg(test)]
mod tests {
use std::fmt;
use super::*;
use crate::{DynTemplate, Template};
#[test]
fn dyn_template() {
struct Test;
impl Template for Test {
fn render_into(&self, writer: &mut (impl std::fmt::Write + ?Sized)) -> Result<()> {
Ok(writer.write_str("test")?)
}
const EXTENSION: Option<&'static str> = Some("txt");
const SIZE_HINT: usize = 4;
const MIME_TYPE: &'static str = "text/plain; charset=utf-8";
}
impl fmt::Display for Test {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.render_into(f).map_err(|_| fmt::Error {})
}
}
fn render(t: &dyn DynTemplate) -> String {
t.dyn_render().unwrap()
}
let test = &Test as &dyn DynTemplate;
assert_eq!(render(test), "test");
assert_eq!(test.to_string(), "test");
assert_eq!(format!("{test}"), "test");
let mut vec = Vec::new();
test.dyn_write_into(&mut vec).unwrap();
assert_eq!(vec, vec![b't', b'e', b's', b't']);
}
}