diff --git a/src/core.rs b/src/core.rs index 4541fae..5aebd1f 100644 --- a/src/core.rs +++ b/src/core.rs @@ -201,5 +201,5 @@ pub trait AnyCast: AnyInfo { /// Implementación automática para cualquier tipo que ya cumpla [`AnyInfo`]. impl AnyCast for T {} -// API para añadir nuevas funcionalidades usando extensiones. +// Infraestructura para ampliar funcionalidades mediante extensiones. pub mod extension; diff --git a/src/core/extension.rs b/src/core/extension.rs index c493cf5..9eb3bd2 100644 --- a/src/core/extension.rs +++ b/src/core/extension.rs @@ -1,4 +1,4 @@ -//! API para añadir nuevas funcionalidades usando extensiones. +//! Infraestructura para ampliar funcionalidades mediante extensiones. //! //! Cada funcionalidad adicional que quiera incorporarse a una aplicación `PageTop` se debe modelar //! como una **extensión**. Todas comparten la misma interfaz declarada en [`ExtensionTrait`]. diff --git a/src/core/extension/definition.rs b/src/core/extension/definition.rs index 625beba..761fcee 100644 --- a/src/core/extension/definition.rs +++ b/src/core/extension/definition.rs @@ -10,8 +10,8 @@ pub type ExtensionRef = &'static dyn ExtensionTrait; /// Interfaz común que debe implementar cualquier extensión de `PageTop`. /// -/// Este *trait* es fácil de implementar, basta con declarar una estructura de tamaño cero para la -/// extensión y sobreescribir los métodos que sea necesario. +/// Este *trait* es fácil de implementar, basta con declarar la estructura de la extensión y +/// sobreescribir los métodos que sea necesario. /// /// ```rust /// use pagetop::prelude::*; diff --git a/src/html.rs b/src/html.rs index 14a89ed..14e72b9 100644 --- a/src/html.rs +++ b/src/html.rs @@ -2,77 +2,3 @@ mod maud; pub use maud::{display, html, html_private, Escaper, Markup, PreEscaped, Render, DOCTYPE}; - -mod assets; -pub use assets::favicon::Favicon; -pub use assets::javascript::JavaScript; -pub use assets::stylesheet::{StyleSheet, TargetMedia}; -pub(crate) use assets::Assets; - -mod context; -pub use context::{Context, ErrorParam}; - -use crate::AutoDefault; - -/// Prepara contenido HTML para su conversión a [`Markup`]. -/// -/// Este tipo encapsula distintos orígenes de contenido HTML (texto plano, HTML escapado o marcado -/// ya procesado) para renderizar de forma homogénea en plantillas sin interferir con el uso -/// estándar de [`Markup`]. -/// -/// # Ejemplo -/// -/// ```rust -/// use pagetop::prelude::*; -/// -/// let fragment = PrepareMarkup::Text(String::from("Hola mundo")); -/// assert_eq!(fragment.render().into_string(), "Hola <b>mundo</b>"); -/// -/// let raw_html = PrepareMarkup::Escaped(String::from("negrita")); -/// assert_eq!(raw_html.render().into_string(), "negrita"); -/// -/// let prepared = PrepareMarkup::With(html! { -/// h2 { "Título de ejemplo" } -/// p { "Este es un párrafo con contenido dinámico." } -/// }); -/// assert_eq!( -/// prepared.render().into_string(), -/// "

Título de ejemplo

Este es un párrafo con contenido dinámico.

" -/// ); -/// ``` -#[derive(AutoDefault)] -pub enum PrepareMarkup { - /// No se genera contenido HTML (devuelve `html! {}`). - #[default] - None, - /// Texto estático que se escapará automáticamente para no ser interpretado como HTML. - Text(String), - /// Contenido sin escapado adicional, útil para HTML generado externamente. - Escaped(String), - /// Fragmento HTML ya preparado como [`Markup`], listo para insertarse directamente. - With(Markup), -} - -impl PrepareMarkup { - /// Devuelve `true` si el contenido está vacío y no generará HTML al renderizar. - pub fn is_empty(&self) -> bool { - match self { - PrepareMarkup::None => true, - PrepareMarkup::Text(text) => text.is_empty(), - PrepareMarkup::Escaped(string) => string.is_empty(), - PrepareMarkup::With(markup) => markup.is_empty(), - } - } -} - -impl Render for PrepareMarkup { - /// Integra el renderizado fácilmente en la macro [`html!`]. - fn render(&self) -> Markup { - match self { - PrepareMarkup::None => html! {}, - PrepareMarkup::Text(text) => html! { (text) }, - PrepareMarkup::Escaped(string) => html! { (PreEscaped(string)) }, - PrepareMarkup::With(markup) => html! { (markup) }, - } - } -} diff --git a/src/html/assets.rs b/src/html/assets.rs deleted file mode 100644 index 894b7e8..0000000 --- a/src/html/assets.rs +++ /dev/null @@ -1,63 +0,0 @@ -pub mod favicon; -pub mod javascript; -pub mod stylesheet; - -use crate::html::{html, Markup, Render}; -use crate::{AutoDefault, Weight}; - -pub trait AssetsTrait: Render { - // Devuelve el nombre del recurso, utilizado como clave única. - fn name(&self) -> &str; - - // Devuelve el peso del recurso, durante el renderizado se procesan de menor a mayor peso. - fn weight(&self) -> Weight; -} - -#[derive(AutoDefault)] -pub(crate) struct Assets(Vec); - -impl Assets { - pub fn new() -> Self { - Assets::(Vec::::new()) - } - - pub fn add(&mut self, asset: T) -> bool { - match self.0.iter().position(|x| x.name() == asset.name()) { - Some(index) => { - if self.0[index].weight() > asset.weight() { - self.0.remove(index); - self.0.push(asset); - true - } else { - false - } - } - _ => { - self.0.push(asset); - true - } - } - } - - pub fn remove(&mut self, name: impl AsRef) -> bool { - if let Some(index) = self.0.iter().position(|x| x.name() == name.as_ref()) { - self.0.remove(index); - true - } else { - false - } - } -} - -impl Render for Assets { - fn render(&self) -> Markup { - let mut assets = self.0.iter().collect::>(); - assets.sort_by_key(|a| a.weight()); - - html! { - @for a in assets { - (a.render()) - } - } - } -} diff --git a/src/html/assets/favicon.rs b/src/html/assets/favicon.rs deleted file mode 100644 index 97a44e8..0000000 --- a/src/html/assets/favicon.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::html::{html, Markup, Render}; -use crate::AutoDefault; - -/// Un **Favicon** es un recurso gráfico que usa el navegador como icono asociado al sitio. -/// -/// Es universalmente aceptado para mostrar el icono del sitio (`.ico`, `.png`, `.svg`, …) en -/// pestañas, marcadores o accesos directos. -/// -/// Este tipo permite construir de forma fluida las distintas variantes de un *favicon*, ya sea un -/// icono estándar, un icono Apple para la pantalla de inicio, o un icono para Safari con color. -/// También puede aplicar colores al tema o configuraciones específicas para *tiles* de Windows. -/// -/// > **Nota** -/// > Los archivos de los iconos deben estar disponibles en el servidor web de la aplicación. Pueden -/// > incluirse en el proyecto utilizando [`include_files`](crate::include_files) y servirse con -/// > [`include_files_service`](crate::include_files_service). -/// -/// # Ejemplo -/// -/// ```rust -/// use pagetop::prelude::*; -/// -/// let favicon = Favicon::new() -/// // Estándar de facto admitido por todos los navegadores. -/// .with_icon("/icons/favicon.ico") -/// -/// // Variante del favicon con tamaños explícitos: 32×32 y 16×16. -/// .with_icon_for_sizes("/icons/favicon-32.png", "32x32") -/// .with_icon_for_sizes("/icons/favicon-16.png", "16x16") -/// -/// // Icono específico para accesos directos en la pantalla de inicio de iOS. -/// .with_apple_touch_icon("/icons/apple-touch-icon.png", "180x180") -/// -/// // Icono vectorial con color dinámico para pestañas ancladas en Safari. -/// .with_mask_icon("/icons/safari-pinned-tab.svg", "#5bbad5") -/// -/// // Personaliza la barra superior del navegador en Android Chrome (y soportado en otros). -/// .with_theme_color("#ffffff") -/// -/// // Personalizaciones específicas para "tiles" en Windows. -/// .with_ms_tile_color("#da532c") -/// .with_ms_tile_image("/icons/mstile-144x144.png"); -/// ``` -#[derive(AutoDefault)] -pub struct Favicon(Vec); - -impl Favicon { - /// Crea un nuevo `Favicon` vacío. - /// - /// Equivalente a `Favicon::default()`. Se recomienda iniciar la secuencia de configuración - /// desde aquí. - pub fn new() -> Self { - Favicon::default() - } - - // Favicon BUILDER ***************************************************************************** - - /// Le añade un icono genérico apuntando a `image`. El tipo MIME se infiere automáticamente a - /// partir de la extensión. - pub fn with_icon(self, image: impl Into) -> Self { - self.add_icon_item("icon", image.into(), None, None) - } - - /// Le añade un icono genérico con atributo `sizes`, útil para indicar resoluciones específicas. - /// - /// El atributo `sizes` informa al navegador de las dimensiones de la imagen para que seleccione - /// el recurso más adecuado. Puede enumerar varias dimensiones separadas por espacios, p.ej. - /// `"16x16 32x32 48x48"` o usar `any` para iconos escalables (SVG). - /// - /// No es imprescindible, pero puede mejorar la selección del icono más adecuado. - pub fn with_icon_for_sizes(self, image: impl Into, sizes: impl Into) -> Self { - self.add_icon_item("icon", image.into(), Some(sizes.into()), None) - } - - /// Le añade un *Apple Touch Icon*, usado por dispositivos iOS para las pantallas de inicio. - /// - /// Se recomienda indicar también el tamaño, p.ej. `"256x256"`. - pub fn with_apple_touch_icon(self, image: impl Into, sizes: impl Into) -> Self { - self.add_icon_item("apple-touch-icon", image.into(), Some(sizes.into()), None) - } - - /// Le añade un icono para el navegador Safari, con un color dinámico. - /// - /// El atributo `color` lo usa Safari para colorear el trazado SVG cuando el icono se muestra en - /// modo *Pinned Tab*. Aunque Safari 12+ acepta *favicons normales*, este método garantiza - /// compatibilidad con versiones anteriores. - pub fn with_mask_icon(self, image: impl Into, color: impl Into) -> Self { - self.add_icon_item("mask-icon", image.into(), None, Some(color.into())) - } - - /// Define el color del tema (``). - /// - /// Lo usan algunos navegadores para colorear la barra de direcciones o interfaces. - pub fn with_theme_color(mut self, color: impl Into) -> Self { - self.0.push(html! { - meta name="theme-color" content=(color.into()); - }); - self - } - - /// Define el color del *tile* en Windows (``). - pub fn with_ms_tile_color(mut self, color: impl Into) -> Self { - self.0.push(html! { - meta name="msapplication-TileColor" content=(color.into()); - }); - self - } - - /// Define la imagen del *tile* en Windows (``). - pub fn with_ms_tile_image(mut self, image: impl Into) -> Self { - self.0.push(html! { - meta name="msapplication-TileImage" content=(image.into()); - }); - self - } - - // Función interna que centraliza la creación de las etiquetas ``. - // - // - `icon_rel`: indica el tipo de recurso (`"icon"`, `"apple-touch-icon"`, etc.). - // - `icon_source`: URL del recurso. - // - `icon_sizes`: tamaños opcionales. - // - `icon_color`: color opcional (solo relevante para `mask-icon`). - // - // También infiere automáticamente el tipo MIME (`type`) según la extensión del archivo. - fn add_icon_item( - mut self, - icon_rel: &str, - icon_source: String, - icon_sizes: Option, - icon_color: Option, - ) -> Self { - let icon_type = match icon_source.rfind('.') { - Some(i) => match icon_source[i..].to_owned().to_lowercase().as_str() { - ".avif" => Some("image/avif"), - ".gif" => Some("image/gif"), - ".ico" => Some("image/x-icon"), - ".jpg" | ".jpeg" => Some("image/jpeg"), - ".png" => Some("image/png"), - ".svg" => Some("image/svg+xml"), - ".webp" => Some("image/webp"), - _ => None, - }, - _ => None, - }; - self.0.push(html! { - link - rel=(icon_rel) - type=[(icon_type)] - sizes=[(icon_sizes)] - color=[(icon_color)] - href=(icon_source); - }); - self - } -} - -impl Render for Favicon { - fn render(&self) -> Markup { - html! { - @for item in &self.0 { - (item) - } - } - } -} diff --git a/src/html/assets/javascript.rs b/src/html/assets/javascript.rs deleted file mode 100644 index fb0a1b6..0000000 --- a/src/html/assets/javascript.rs +++ /dev/null @@ -1,178 +0,0 @@ -use crate::html::assets::AssetsTrait; -use crate::html::{html, Markup, Render}; -use crate::{join, join_pair, AutoDefault, Weight}; - -// Define el origen del recurso JavaScript y cómo debe cargarse en el navegador. -// -// Los distintos modos de carga permiten optimizar el rendimiento y controlar el comportamiento del -// script. -// -// - [`From`] – Carga el script de forma estándar con la etiqueta ``. El parámetro `name` se usa como identificador interno del - /// *script*. - pub fn inline(name: impl Into, script: impl Into) -> Self { - JavaScript { - source: Source::Inline(name.into(), script.into()), - ..Default::default() - } - } - - /// Crea un **script embebido** que se ejecuta automáticamente al terminar de cargarse el - /// documento HTML. - /// - /// El código se envuelve automáticamente en un `addEventListener('DOMContentLoaded', ...)`. El - /// parámetro `name` se usa como identificador interno del *script*. - pub fn on_load(name: impl Into, script: impl Into) -> Self { - JavaScript { - source: Source::OnLoad(name.into(), script.into()), - ..Default::default() - } - } - - // JavaScript BUILDER ************************************************************************** - - /// Asocia una versión al recurso (usada para control de la caché del navegador). - /// - /// Si `version` está vacío, no se añade ningún parámetro a la URL. - pub fn with_version(mut self, version: impl Into) -> Self { - self.version = version.into(); - self - } - - /// Modifica el peso del recurso. - /// - /// Los recursos se renderizan de menor a mayor peso. Por defecto es `0`, que respeta el orden - /// de creación. - pub fn with_weight(mut self, value: Weight) -> Self { - self.weight = value; - self - } -} - -impl AssetsTrait for JavaScript { - // Para *scripts* externos es la ruta; para *scripts* embebidos, un identificador. - fn name(&self) -> &str { - match &self.source { - Source::From(path) => path, - Source::Defer(path) => path, - Source::Async(path) => path, - Source::Inline(name, _) => name, - Source::OnLoad(name, _) => name, - } - } - - fn weight(&self) -> Weight { - self.weight - } -} - -impl Render for JavaScript { - fn render(&self) -> Markup { - match &self.source { - Source::From(path) => html! { - script src=(join_pair!(path, "?v=", self.version.as_str())) {}; - }, - Source::Defer(path) => html! { - script src=(join_pair!(path, "?v=", self.version.as_str())) defer {}; - }, - Source::Async(path) => html! { - script src=(join_pair!(path, "?v=", self.version.as_str())) async {}; - }, - Source::Inline(_, code) => html! { - script { (code) }; - }, - Source::OnLoad(_, code) => html! { (join!( - "document.addEventListener('DOMContentLoaded',function(){", code, "});" - )) }, - } - } -} diff --git a/src/html/assets/stylesheet.rs b/src/html/assets/stylesheet.rs deleted file mode 100644 index 7f64d18..0000000 --- a/src/html/assets/stylesheet.rs +++ /dev/null @@ -1,174 +0,0 @@ -use crate::html::assets::AssetsTrait; -use crate::html::{html, Markup, PreEscaped, Render}; -use crate::{join_pair, AutoDefault, Weight}; - -// Define el origen del recurso CSS y cómo se incluye en el documento. -// -// Los estilos pueden cargarse desde un archivo externo o estar embebidos directamente en una -// etiqueta ``. El parámetro `name` se usa como identificador interno del - /// recurso. - pub fn inline(name: impl Into, styles: impl Into) -> Self { - StyleSheet { - source: Source::Inline(name.into(), styles.into()), - ..Default::default() - } - } - - // StyleSheet BUILDER ************************************************************************** - - /// Asocia una versión al recurso (usada para control de la caché del navegador). - /// - /// Si `version` está vacío, no se añade ningún parámetro a la URL. - pub fn with_version(mut self, version: impl Into) -> Self { - self.version = version.into(); - self - } - - /// Modifica el peso del recurso. - /// - /// Los recursos se renderizan de menor a mayor peso. Por defecto es `0`, que respeta el orden - /// de creación. - pub fn with_weight(mut self, value: Weight) -> Self { - self.weight = value; - self - } - - // StyleSheet EXTRAS *************************************************************************** - - /// Especifica el medio donde se aplican los estilos. - /// - /// Según el argumento `media`: - /// - /// - `TargetMedia::Default` - Se aplica en todos los casos (medio por defecto). - /// - `TargetMedia::Print` - Se aplican cuando el documento se imprime. - /// - `TargetMedia::Screen` - Se aplican en pantallas. - /// - `TargetMedia::Speech` - Se aplican en dispositivos que convierten el texto a voz. - pub fn for_media(mut self, media: TargetMedia) -> Self { - self.media = media; - self - } -} - -impl AssetsTrait for StyleSheet { - // Para hojas de estilos externas es la ruta; para las embebidas, un identificador. - fn name(&self) -> &str { - match &self.source { - Source::From(path) => path, - Source::Inline(name, _) => name, - } - } - - fn weight(&self) -> Weight { - self.weight - } -} - -impl Render for StyleSheet { - fn render(&self) -> Markup { - match &self.source { - Source::From(path) => html! { - link - rel="stylesheet" - href=(join_pair!(path, "?v=", self.version.as_str())) - media=[self.media.as_str_opt()]; - }, - Source::Inline(_, code) => html! { - style { (PreEscaped(code)) }; - }, - } - } -} diff --git a/src/html/context.rs b/src/html/context.rs deleted file mode 100644 index 23e2be2..0000000 --- a/src/html/context.rs +++ /dev/null @@ -1,216 +0,0 @@ -use crate::core::TypeInfo; -use crate::html::{html, Markup, Render}; -use crate::html::{Assets, Favicon, JavaScript, StyleSheet}; -use crate::join; -use crate::locale::{LanguageIdentifier, DEFAULT_LANGID}; -use crate::service::HttpRequest; - -use std::collections::HashMap; -use std::error::Error; -use std::str::FromStr; - -use std::fmt; - -/// Errores de lectura o conversión de parámetros almacenados en el contexto. -#[derive(Debug)] -pub enum ErrorParam { - /// El parámetro solicitado no existe. - NotFound, - /// El valor del parámetro no pudo convertirse al tipo requerido. - ParseError(String), -} - -impl fmt::Display for ErrorParam { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ErrorParam::NotFound => write!(f, "Parameter not found"), - ErrorParam::ParseError(e) => write!(f, "Parse error: {e}"), - } - } -} - -impl Error for ErrorParam {} - -/// Representa el contexto asociado a un documento HTML. -/// -/// Esta estructura se crea internamente para recoger información relativa al documento asociado, -/// como la solicitud HTTP de origen, el idioma y los recursos *favicon* ([`Favicon`]), las hojas de -/// estilo [`StyleSheet`], los *scripts* [`JavaScript`], o parámetros de contexto definidos en -/// tiempo de ejecución. -/// -/// # Ejemplo -/// -/// ```rust -/// use pagetop::prelude::*; -/// -/// fn configure_context(mut ctx: Context) { -/// // Establece el idioma del documento a español. -/// ctx.set_langid(LangMatch::langid_or_default("es-ES")); -/// -/// // Asigna un favicon. -/// ctx.set_favicon(Some(Favicon::new().with_icon("/icons/favicon.ico"))); -/// -/// // Añade una hoja de estilo externa. -/// ctx.add_stylesheet(StyleSheet::from("/css/style.css")); -/// -/// // Añade un script JavaScript. -/// ctx.add_javascript(JavaScript::defer("/js/main.js")); -/// -/// // Añade un parámetro dinámico al contexto. -/// ctx.set_param("usuario_id", 42); -/// -/// // Recupera el parámetro y lo convierte a su tipo original. -/// let id: i32 = ctx.get_param("usuario_id").unwrap(); -/// assert_eq!(id, 42); -/// -/// // Genera un identificador único para un componente de tipo `Menu`. -/// struct Menu; -/// let unique_id = ctx.required_id::(None); -/// assert_eq!(unique_id, "menu-1"); // Si es el primero generado. -/// } -/// ``` -#[rustfmt::skip] -pub struct Context { - request : HttpRequest, // Solicitud HTTP de origen. - langid : &'static LanguageIdentifier, // Identificador del idioma. - favicon : Option, // Favicon, si se ha definido. - stylesheets: Assets, // Hojas de estilo CSS. - javascripts: Assets, // Scripts JavaScript. - params : HashMap, // Parámetros definidos en tiempo de ejecución. - id_counter : usize, // Contador para generar identificadores únicos. -} - -impl Context { - // Crea un nuevo contexto asociado a una solicitud HTTP. - // - // El contexto inicializa el idioma por defecto, sin favicon ni recursos cargados. - #[rustfmt::skip] - pub(crate) fn new(request: HttpRequest) -> Self { - Context { - request, - langid : &DEFAULT_LANGID, - favicon : None, - stylesheets: Assets::::new(), - javascripts: Assets::::new(), - params : HashMap::::new(), - id_counter : 0, - } - } - - /// Modifica el identificador de idioma del documento. - pub fn set_langid(&mut self, langid: &'static LanguageIdentifier) -> &mut Self { - self.langid = langid; - self - } - - /// Define el *favicon* del documento. Sobrescribe cualquier valor anterior. - pub fn set_favicon(&mut self, favicon: Option) -> &mut Self { - self.favicon = favicon; - self - } - - /// Define el *favicon* solo si no se ha establecido previamente. - pub fn set_favicon_if_none(&mut self, favicon: Favicon) -> &mut Self { - if self.favicon.is_none() { - self.favicon = Some(favicon); - } - self - } - - /// Añade una hoja de estilos CSS al documento. - pub fn add_stylesheet(&mut self, css: StyleSheet) -> &mut Self { - self.stylesheets.add(css); - self - } - - /// Elimina una hoja de estilos por su ruta o identificador. - pub fn remove_stylesheet(&mut self, name: impl AsRef) -> &mut Self { - self.stylesheets.remove(name); - self - } - - /// Añade un *script* JavaScript al documento. - pub fn add_javascript(&mut self, js: JavaScript) -> &mut Self { - self.javascripts.add(js); - self - } - - /// Elimina un *script* por su ruta o identificador. - pub fn remove_javascript(&mut self, name: impl AsRef) -> &mut Self { - self.javascripts.remove(name); - self - } - - /// Añade o modifica un parámetro del contexto almacenando el valor como [`String`]. - pub fn set_param(&mut self, key: impl AsRef, value: T) -> &mut Self { - self.params - .insert(key.as_ref().to_string(), value.to_string()); - self - } - - // Context GETTERS ***************************************************************************** - - /// Devuelve la solicitud HTTP asociada al documento. - pub fn request(&self) -> &HttpRequest { - &self.request - } - - /// Devuelve el identificador del idioma asociado al documento. - pub fn langid(&self) -> &LanguageIdentifier { - self.langid - } - - /// Recupera un parámetro del contexto convertido al tipo especificado. - /// - /// Devuelve un error si el parámetro no existe ([`ErrorParam::NotFound`]) o la conversión falla - /// ([`ErrorParam::ParseError`]). - pub fn get_param(&self, key: impl AsRef) -> Result { - self.params - .get(key.as_ref()) - .ok_or(ErrorParam::NotFound) - .and_then(|v| T::from_str(v).map_err(|_| ErrorParam::ParseError(v.clone()))) - } - - // Context EXTRAS ****************************************************************************** - - /// Elimina un parámetro del contexto. Devuelve `true` si existía y se eliminó. - pub fn remove_param(&mut self, key: impl AsRef) -> bool { - self.params.remove(key.as_ref()).is_some() - } - - /// Genera un identificador único si no se proporciona uno explícito. - /// - /// Si no se proporciona un `id`, se genera un identificador único en la forma `-` - /// donde `` es el nombre corto del tipo en minúsculas (sin espacios) y `` es un - /// contador interno incremental. - pub fn required_id(&mut self, id: Option) -> String { - if let Some(id) = id { - id - } else { - let prefix = TypeInfo::ShortName - .of::() - .trim() - .replace(' ', "_") - .to_lowercase(); - let prefix = if prefix.is_empty() { - "prefix".to_owned() - } else { - prefix - }; - self.id_counter += 1; - join!(prefix, "-", self.id_counter.to_string()) - } - } -} - -impl Render for Context { - fn render(&self) -> Markup { - html! { - @if let Some(favicon) = &self.favicon { - (favicon) - } - (self.stylesheets) - (self.javascripts) - } - } -} diff --git a/src/html/maud.rs b/src/html/maud.rs index 9bf179e..39cb2c2 100644 --- a/src/html/maud.rs +++ b/src/html/maud.rs @@ -18,7 +18,7 @@ pub use pagetop_macros::html; mod escape; -/// Adaptador que escapa los caracteres especiales de HTML. +/// An adapter that escapes HTML special characters. /// /// The following characters are escaped: /// @@ -57,7 +57,7 @@ impl fmt::Write for Escaper<'_> { } } -/// Representa un tipo que puede renderizarse como HTML. +/// Represents a type that can be rendered as HTML. /// /// To implement this for your own type, override either the `.render()` /// or `.render_to()` methods; since each is defined in terms of the @@ -190,7 +190,7 @@ impl_render_with_itoa! { u8 u16 u32 u64 u128 usize } -/// Renderiza un valor usando su implementación de [`Display`]. +/// Renders a value using its [`Display`] impl. /// /// # Example /// @@ -219,7 +219,7 @@ pub fn display(value: impl Display) -> impl Render { DisplayWrapper(value) } -/// Contenedor que renderiza el valor interno sin escapar. +/// A wrapper that renders the inner value without escaping. #[derive(Debug, Clone, Copy)] pub struct PreEscaped(pub T); @@ -229,7 +229,7 @@ impl> Render for PreEscaped { } } -/// Un bloque de marcado es una cadena que no necesita ser escapada. +/// A block of markup is a string that does not need to be escaped. /// /// The `html!` macro expands to an expression of this type. pub type Markup = PreEscaped; @@ -259,7 +259,7 @@ impl Default for PreEscaped { } } -/// La cadena literal ``. +/// The literal string ``. /// /// # Example /// diff --git a/src/lib.rs b/src/lib.rs index edc791c..248ba6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ //! ``` #![cfg_attr(docsrs, feature(doc_cfg))] + #![doc( html_favicon_url = "https://git.cillero.es/manuelcillero/pagetop/raw/branch/main/static/favicon.ico" )] @@ -65,12 +66,6 @@ impl Deref for StaticResources { } } -/// Representa el peso lógico de una instancia en una colección ordenada por pesos. -/// -/// Las instancias con pesos **más bajos**, incluyendo valores negativos (`-128..127`), se situarán -/// antes en la ordenación. -pub type Weight = i8; - // API ********************************************************************************************* // Funciones y macros útiles. diff --git a/src/locale.rs b/src/locale.rs index 2dcf27e..ab1f5ef 100644 --- a/src/locale.rs +++ b/src/locale.rs @@ -107,8 +107,8 @@ use std::sync::LazyLock; use std::fmt; -// Asocia cada identificador de idioma (como "en-US") con su respectivo [`LanguageIdentifier`] y la -// clave en *locale/.../languages.ftl* para obtener el nombre del idioma según la localización. +// Asocia cada código de idioma (como "en-US") con su respectivo [`LanguageIdentifier`] y la clave +// en *locale/.../languages.ftl* para obtener el nombre del idioma según la localización. static LANGUAGES: LazyLock> = LazyLock::new(|| { hm![ "en" => ( langid!("en-US"), "english" ), @@ -121,20 +121,20 @@ static LANGUAGES: LazyLock> = LazyLock // Identificador del idioma de **respaldo** (predefinido a `en-US`). // -// Se usa cuando el valor del identificador de idioma en las traducciones no corresponde con ningún -// idioma soportado por la aplicación. +// Se usa cuando el valor del código de idioma en las traducciones no corresponde con ningún idioma +// soportado por la aplicación. static FALLBACK_LANGID: LazyLock = LazyLock::new(|| langid!("en-US")); // Identificador del idioma **por defecto** para la aplicación. // -// Se resuelve a partir de [`global::SETTINGS.app.language`](global::SETTINGS). Si el identificador -// de idioma no es válido o no está disponible entonces resuelve como [`FALLBACK_LANGID`]. +// Se resuelve a partir de [`global::SETTINGS.app.language`](global::SETTINGS). Si el código de +// idioma configurado no es válido o no está disponible entonces resuelve como [`FALLBACK_LANGID`]. pub(crate) static DEFAULT_LANGID: LazyLock<&LanguageIdentifier> = LazyLock::new(|| LangMatch::langid_or_fallback(&global::SETTINGS.app.language)); /// Operaciones con los idiomas soportados por `PageTop`. /// -/// Utiliza [`LangMatch`] para transformar un identificador de idioma en un [`LanguageIdentifier`] +/// Utiliza [`LangMatch`] para transformar un código de idioma en un [`LanguageIdentifier`] /// soportado por `PageTop`. /// /// # Ejemplos @@ -156,10 +156,10 @@ pub(crate) static DEFAULT_LANGID: LazyLock<&LanguageIdentifier> = /// /// // Idioma no soportado. /// let lang = LangMatch::resolve("ja-JP"); -/// assert_eq!(lang, LangMatch::Unsupported(String::from("ja-JP"))); +/// assert_eq!(lang, LangMatch::Unsupported("ja-JP".to_string())); /// ``` /// -/// Las siguientes líneas devuelven siempre un [`LanguageIdentifier`] válido, ya sea porque +/// Las siguientes instrucciones devuelven siempre un [`LanguageIdentifier`] válido, ya sea porque /// resuelven un idioma soportado o porque aplican el idioma por defecto o de respaldo: /// /// ```rust @@ -177,13 +177,13 @@ pub(crate) static DEFAULT_LANGID: LazyLock<&LanguageIdentifier> = /// ``` #[derive(Clone, Debug, Eq, PartialEq)] pub enum LangMatch { - /// Cuando el identificador del idioma es una cadena vacía. + /// Cuando el código del idioma es una cadena vacía. Unspecified, /// Si encuentra un [`LanguageIdentifier`] en la lista de idiomas soportados por `PageTop` que - /// coincide exactamente con el identificador del idioma (p.ej. "es-ES"), o con el identificador - /// del idioma base (p.ej. "es"). + /// coincide exactamente con el código del idioma (p.ej. "es-ES"), o con el código del idioma + /// base (p.ej. "es"). Found(&'static LanguageIdentifier), - /// Si el identificador del idioma no está entre los soportados por `PageTop`. + /// Si el código del idioma no está entre los soportados por `PageTop`. Unsupported(String), } @@ -210,7 +210,7 @@ impl LangMatch { } // En otro caso indica que el idioma no está soportado. - Self::Unsupported(String::from(language)) + Self::Unsupported(language.to_string()) } /// Devuelve el idioma de la variante de la instancia, o el idioma por defecto si no está @@ -319,7 +319,7 @@ enum L10nOp { /// También para traducciones a idiomas concretos. /// /// ```rust,ignore -/// // Traducción con clave, conjunto de traducciones e identificador de idioma a usar. +/// // Traducción con clave, conjunto de traducciones y código de idioma a usar. /// let bye = L10n::t("goodbye", &LOCALES_CUSTOM).using(LangMatch::langid_or_default("it")); /// ``` #[derive(AutoDefault)] diff --git a/src/prelude.rs b/src/prelude.rs index 3a9f4f6..6506aaf 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,7 +4,7 @@ pub use crate::{builder_fn, html, main, test}; -pub use crate::{AutoDefault, StaticResources, Weight}; +pub use crate::{AutoDefault, StaticResources}; // MACROS. @@ -16,8 +16,6 @@ pub use crate::include_config; pub use crate::include_locales; // crate::service pub use crate::{include_files, include_files_service}; -// crate::core::action -//pub use crate::actions; // API. @@ -37,7 +35,6 @@ pub use crate::service; pub use crate::core::{AnyCast, AnyInfo, TypeInfo}; -//pub use crate::core::action::*; pub use crate::core::extension::*; pub use crate::app::Application; diff --git a/src/service.rs b/src/service.rs index 47e8422..9e506fa 100644 --- a/src/service.rs +++ b/src/service.rs @@ -6,7 +6,7 @@ pub use actix_web::dev::ServiceFactory as Factory; pub use actix_web::dev::ServiceRequest as Request; pub use actix_web::dev::ServiceResponse as Response; pub use actix_web::{http, rt, web}; -pub use actix_web::{App, Error, HttpRequest, HttpServer}; +pub use actix_web::{App, Error, HttpServer}; #[doc(hidden)] pub use actix_web::test; diff --git a/tests/html.rs b/tests/html.rs deleted file mode 100644 index 315f74a..0000000 --- a/tests/html.rs +++ /dev/null @@ -1,17 +0,0 @@ -use pagetop::prelude::*; - -#[pagetop::test] -async fn prepare_markup_is_empty() { - let _app = service::test::init_service(Application::new().test()).await; - - assert!(PrepareMarkup::None.is_empty()); - - assert!(PrepareMarkup::Text(String::from("")).is_empty()); - assert!(!PrepareMarkup::Text(String::from("x")).is_empty()); - - assert!(PrepareMarkup::Escaped(String::new()).is_empty()); - assert!(!PrepareMarkup::Escaped("a".into()).is_empty()); - - assert!(PrepareMarkup::With(html! {}).is_empty()); - assert!(!PrepareMarkup::With(html! { span { "!" } }).is_empty()); -}