// #![no_std] //! A macro for writing HTML templates. //! //! This documentation only describes the runtime API. For a general //! guide, check out the [book] instead. //! //! [book]: https://maud.lambda.xyz/ // #![doc(html_root_url = "https://docs.rs/maud/0.27.0")] extern crate alloc; use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc}; use core::fmt::{self, Arguments, Display, Write}; pub use pagetop_macros::html; mod escape; /// Adaptador que escapa los caracteres especiales de HTML. /// /// The following characters are escaped: /// /// * `&` is escaped as `&` /// * `<` is escaped as `<` /// * `>` is escaped as `>` /// * `"` is escaped as `"` /// /// All other characters are passed through unchanged. /// /// **Note:** In versions prior to 0.13, the single quote (`'`) was /// escaped as well. /// /// # Example /// /// ```rust /// use pagetop::html::Escaper; /// use std::fmt::Write; /// let mut s = String::new(); /// write!(Escaper::new(&mut s), "").unwrap(); /// assert_eq!(s, "<script>launchMissiles()</script>"); /// ``` pub struct Escaper<'a>(&'a mut String); impl<'a> Escaper<'a> { /// Creates an `Escaper` from a `String`. pub fn new(buffer: &'a mut String) -> Escaper<'a> { Escaper(buffer) } } impl fmt::Write for Escaper<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { escape::escape_to_string(s, self.0); Ok(()) } } /// Representa un tipo que puede renderizarse como HTML. /// /// To implement this for your own type, override either the `.render()` /// or `.render_to()` methods; since each is defined in terms of the /// other, you only need to implement one of them. See the example below. /// /// # Minimal implementation /// /// An implementation of this trait must override at least one of /// `.render()` or `.render_to()`. Since the default definitions of /// these methods call each other, not doing this will result in /// infinite recursion. /// /// # Example /// /// ```rust /// use pagetop::prelude::*; /// /// /// Provides a shorthand for linking to a CSS stylesheet. /// pub struct Stylesheet(&'static str); /// /// impl Render for Stylesheet { /// fn render(&self) -> Markup { /// html! { /// link rel="stylesheet" type="text/css" href=(self.0); /// } /// } /// } /// ``` pub trait Render { /// Renders `self` as a block of `Markup`. fn render(&self) -> Markup { let mut buffer = String::new(); self.render_to(&mut buffer); PreEscaped(buffer) } /// Appends a representation of `self` to the given buffer. /// /// Its default implementation just calls `.render()`, but you may /// override it with something more efficient. /// /// Note that no further escaping is performed on data written to /// the buffer. If you override this method, you must make sure that /// any data written is properly escaped, whether by hand or using /// the [`Escaper`](struct.Escaper.html) wrapper struct. fn render_to(&self, buffer: &mut String) { buffer.push_str(&self.render().into_string()); } } impl Render for str { fn render_to(&self, w: &mut String) { escape::escape_to_string(self, w); } } impl Render for String { fn render_to(&self, w: &mut String) { str::render_to(self, w); } } impl Render for Cow<'_, str> { fn render_to(&self, w: &mut String) { str::render_to(self, w); } } impl Render for Arguments<'_> { fn render_to(&self, w: &mut String) { let _ = Escaper::new(w).write_fmt(*self); } } impl Render for &T { fn render_to(&self, w: &mut String) { T::render_to(self, w); } } impl Render for &mut T { fn render_to(&self, w: &mut String) { T::render_to(self, w); } } impl Render for Box { fn render_to(&self, w: &mut String) { T::render_to(self, w); } } impl Render for Arc { fn render_to(&self, w: &mut String) { T::render_to(self, w); } } macro_rules! impl_render_with_display { ($($ty:ty)*) => { $( impl Render for $ty { fn render_to(&self, w: &mut String) { // TODO: remove the explicit arg when Rust 1.58 is released format_args!("{self}", self = self).render_to(w); } } )* }; } impl_render_with_display! { char f32 f64 } macro_rules! impl_render_with_itoa { ($($ty:ty)*) => { $( impl Render for $ty { fn render_to(&self, w: &mut String) { w.push_str(itoa::Buffer::new().format(*self)); } } )* }; } impl_render_with_itoa! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } /// Renderiza un valor usando su implementación de [`Display`]. /// /// # Example /// /// ```rust /// use pagetop::prelude::*; /// use std::net::Ipv4Addr; /// /// let ip_address = Ipv4Addr::new(127, 0, 0, 1); /// /// let markup = html! { /// "My IP address is: " /// (display(ip_address)) /// }; /// /// assert_eq!(markup.into_string(), "My IP address is: 127.0.0.1"); /// ``` pub fn display(value: impl Display) -> impl Render { struct DisplayWrapper(T); impl Render for DisplayWrapper { fn render_to(&self, w: &mut String) { format_args!("{0}", self.0).render_to(w); } } DisplayWrapper(value) } /// Contenedor que renderiza el valor interno sin escapar. #[derive(Debug, Clone, Copy)] pub struct PreEscaped(pub T); impl> Render for PreEscaped { fn render_to(&self, w: &mut String) { w.push_str(self.0.as_ref()); } } /// Un bloque de marcado es una cadena que no necesita ser escapada. /// /// The `html!` macro expands to an expression of this type. pub type Markup = PreEscaped; impl Markup { pub fn is_empty(&self) -> bool { self.0.is_empty() } } impl> PreEscaped { /// Converts the inner value to a string. pub fn into_string(self) -> String { self.0.into() } } impl> From> for String { fn from(value: PreEscaped) -> String { value.into_string() } } impl Default for PreEscaped { fn default() -> Self { Self(Default::default()) } } /// La cadena literal ``. /// /// # Example /// /// A minimal web page: /// /// ```rust /// use pagetop::prelude::*; /// /// let markup = html! { /// (DOCTYPE) /// html { /// head { /// meta charset="utf-8"; /// title { "Test page" } /// } /// body { /// p { "Hello, world!" } /// } /// } /// }; /// ``` pub const DOCTYPE: PreEscaped<&'static str> = PreEscaped(""); mod actix_support { extern crate alloc; use core::{ pin::Pin, task::{Context, Poll}, }; use crate::html::PreEscaped; use actix_web::{ body::{BodySize, MessageBody}, http::header, web::Bytes, HttpRequest, HttpResponse, Responder, }; use alloc::string::String; impl MessageBody for PreEscaped { type Error = ::Error; fn size(&self) -> BodySize { self.0.size() } fn poll_next( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { Pin::new(&mut self.0).poll_next(cx) } } impl Responder for PreEscaped { type Body = String; fn respond_to(self, _req: &HttpRequest) -> HttpResponse { HttpResponse::Ok() .content_type(header::ContentType::html()) .message_body(self.0) .unwrap() } } } #[doc(hidden)] pub mod html_private { extern crate alloc; use super::{display, Render}; use alloc::string::String; use core::fmt::Display; #[doc(hidden)] #[macro_export] macro_rules! render_to { ($x:expr, $buffer:expr) => {{ use $crate::html::html_private::*; match ChooseRenderOrDisplay($x) { x => (&&x).implements_render_or_display().render_to(x.0, $buffer), } }}; } pub use render_to; pub struct ChooseRenderOrDisplay(pub T); pub struct ViaRenderTag; pub struct ViaDisplayTag; pub trait ViaRender { fn implements_render_or_display(&self) -> ViaRenderTag { ViaRenderTag } } pub trait ViaDisplay { fn implements_render_or_display(&self) -> ViaDisplayTag { ViaDisplayTag } } impl ViaRender for &ChooseRenderOrDisplay {} impl ViaDisplay for ChooseRenderOrDisplay {} impl ViaRenderTag { pub fn render_to(self, value: &T, buffer: &mut String) { value.render_to(buffer); } } impl ViaDisplayTag { pub fn render_to(self, value: &T, buffer: &mut String) { display(value).render_to(buffer); } } }