#![no_std]
#![doc(html_root_url = "https://docs.rs/maud/0.23.0")]
extern crate alloc;
use alloc::string::String;
use core::fmt::{self, Write};
pub use maud_macros::{html, html_debug};
mod escape;
pub struct Escaper<'a>(&'a mut String);
impl<'a> Escaper<'a> {
pub fn new(buffer: &'a mut String) -> Escaper<'a> {
Escaper(buffer)
}
}
impl<'a> fmt::Write for Escaper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
escape::escape_to_string(s, self.0);
Ok(())
}
}
pub trait Render {
fn render(&self) -> Markup {
let mut buffer = String::new();
self.render_to(&mut buffer);
PreEscaped(buffer)
}
fn render_to(&self, buffer: &mut String) {
buffer.push_str(&self.render().into_string());
}
}
impl<T: fmt::Display + ?Sized> Render for T {
fn render_to(&self, w: &mut String) {
let _ = write!(Escaper::new(w), "{}", self);
}
}
#[doc(hidden)]
pub mod render {
use crate::{Escaper, Render};
use alloc::string::String;
use core::fmt::Write;
pub trait RenderInternal {
fn __maud_render_to(&self, w: &mut String);
}
pub struct RenderWrapper<'a, T: ?Sized>(pub &'a T);
impl<'a, T: AsRef<str> + ?Sized> RenderWrapper<'a, T> {
pub fn __maud_render_to(&self, w: &mut String) {
let _ = Escaper::new(w).write_str(self.0.as_ref());
}
}
impl<'a, T: Render + ?Sized> RenderInternal for RenderWrapper<'a, T> {
fn __maud_render_to(&self, w: &mut String) {
self.0.render_to(w);
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct PreEscaped<T: AsRef<str>>(pub T);
impl<T: AsRef<str>> Render for PreEscaped<T> {
fn render_to(&self, w: &mut String) {
w.push_str(self.0.as_ref());
}
}
pub type Markup = PreEscaped<String>;
impl<T: AsRef<str> + Into<String>> PreEscaped<T> {
pub fn into_string(self) -> String {
self.0.into()
}
}
impl<T: AsRef<str> + Into<String>> From<PreEscaped<T>> for String {
fn from(value: PreEscaped<T>) -> String {
value.into_string()
}
}
pub const DOCTYPE: PreEscaped<&'static str> = PreEscaped("<!DOCTYPE html>");
#[cfg(feature = "rocket")]
mod rocket_support {
extern crate std;
use crate::PreEscaped;
use alloc::string::String;
use rocket::{
http::{ContentType, Status},
request::Request,
response::{Responder, Response},
};
use std::io::Cursor;
impl Responder<'static> for PreEscaped<String> {
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
Response::build()
.header(ContentType::HTML)
.sized_body(Cursor::new(self.0))
.ok()
}
}
}
#[cfg(feature = "actix-web")]
mod actix_support {
use crate::PreEscaped;
use actix_web_dep::{Error, HttpRequest, HttpResponse, Responder};
use alloc::string::String;
use futures_util::future::{ok, Ready};
impl Responder for PreEscaped<String> {
type Error = Error;
type Future = Ready<Result<HttpResponse, Self::Error>>;
fn respond_to(self, _req: &HttpRequest) -> Self::Future {
ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(self.0))
}
}
}
#[cfg(feature = "tide")]
mod tide_support {
use crate::PreEscaped;
use alloc::string::String;
use tide::{http::mime, Response, StatusCode};
impl From<PreEscaped<String>> for Response {
fn from(markup: PreEscaped<String>) -> Response {
Response::builder(StatusCode::Ok)
.body(markup.into_string())
.content_type(mime::HTML)
.build()
}
}
}
#[cfg(feature = "axum")]
mod axum_support {
use crate::PreEscaped;
use alloc::string::String;
use axum::{
body::Body,
http::{header, HeaderValue, Response, StatusCode},
response::IntoResponse,
};
impl IntoResponse for PreEscaped<String> {
type Body = Body;
type BodyError = <Self::Body as axum::body::HttpBody>::Error;
fn into_response(self) -> Response<Body> {
let mut res = Response::new(Body::from(self.0));
*res.status_mut() = StatusCode::OK;
res.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("text/html; charset=utf-8"),
);
res
}
}
}