From 86e4c4f1106204a13ed9c0e81dd60a2e56a62206 Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sun, 20 Jul 2025 23:51:15 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20A=C3=B1ade=20soporte=20a=20temas=20?= =?UTF-8?q?en=20la=20API=20de=20extensiones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Incluye una opción de configuración para definir el tema por defecto. - Añade un tema básico predeterminado. --- config/default.toml | 2 ++ src/base.rs | 3 +++ src/base/theme.rs | 4 ++++ src/base/theme/basic.rs | 14 ++++++++++++++ src/core.rs | 5 ++++- src/core/extension/all.rs | 19 +++++++++++++++++++ src/core/extension/definition.rs | 26 ++++++++++++++++++++++++++ src/core/theme.rs | 6 ++++++ src/core/theme/all.rs | 31 +++++++++++++++++++++++++++++++ src/core/theme/definition.rs | 30 ++++++++++++++++++++++++++++++ src/global.rs | 3 +++ src/lib.rs | 4 +++- src/prelude.rs | 6 +++--- 13 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 config/default.toml create mode 100644 src/base.rs create mode 100644 src/base/theme.rs create mode 100644 src/base/theme/basic.rs create mode 100644 src/core/theme.rs create mode 100644 src/core/theme/all.rs create mode 100644 src/core/theme/definition.rs diff --git a/config/default.toml b/config/default.toml new file mode 100644 index 0000000..3e25458 --- /dev/null +++ b/config/default.toml @@ -0,0 +1,2 @@ +[log] +tracing = "Info,pagetop=Debug" diff --git a/src/base.rs b/src/base.rs new file mode 100644 index 0000000..c50cc9e --- /dev/null +++ b/src/base.rs @@ -0,0 +1,3 @@ +//! Reúne temas listos para usar. + +pub mod theme; diff --git a/src/base/theme.rs b/src/base/theme.rs new file mode 100644 index 0000000..ea9eeb6 --- /dev/null +++ b/src/base/theme.rs @@ -0,0 +1,4 @@ +//! Temas básicos soportados por `PageTop`. + +mod basic; +pub use basic::Basic; diff --git a/src/base/theme/basic.rs b/src/base/theme/basic.rs new file mode 100644 index 0000000..0fc533c --- /dev/null +++ b/src/base/theme/basic.rs @@ -0,0 +1,14 @@ +//! Es el tema básico que incluye `PageTop` por defecto. + +use crate::prelude::*; + +/// Tema básico por defecto. +pub struct Basic; + +impl ExtensionTrait for Basic { + fn theme(&self) -> Option { + Some(&Self) + } +} + +impl ThemeTrait for Basic {} diff --git a/src/core.rs b/src/core.rs index 4541fae..9f3d7b5 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,4 +1,4 @@ -//! Tipos y funciones esenciales para crear extensiones. +//! Tipos y funciones esenciales para crear extensiones y temas. use std::any::Any; @@ -203,3 +203,6 @@ impl AnyCast for T {} // API para añadir nuevas funcionalidades usando extensiones. pub mod extension; + +// API para añadir y gestionar nuevos temas. +pub mod theme; diff --git a/src/core/extension/all.rs b/src/core/extension/all.rs index d5bebda..63a178c 100644 --- a/src/core/extension/all.rs +++ b/src/core/extension/all.rs @@ -1,4 +1,5 @@ use crate::core::extension::ExtensionRef; +use crate::core::theme::all::THEMES; use crate::{service, trace}; use std::sync::{LazyLock, RwLock}; @@ -17,6 +18,9 @@ pub fn register_extensions(root_extension: Option) { // Prepara la lista de extensiones habilitadas. let mut enabled_list: Vec = Vec::new(); + // Primero añade el tema básico a la lista de extensiones habilitadas. + add_to_enabled(&mut enabled_list, &crate::base::theme::Basic); + // Si se proporciona una extensión raíz inicial, se añade a la lista de extensiones habilitadas. if let Some(extension) = root_extension { add_to_enabled(&mut enabled_list, extension); @@ -53,6 +57,21 @@ fn add_to_enabled(list: &mut Vec, extension: ExtensionRef) { // Añade la propia extensión a la lista. list.push(extension); + + // Comprueba si la extensión tiene un tema asociado que deba registrarse. + if let Some(theme) = extension.theme() { + let mut registered_themes = THEMES.write().unwrap(); + // Asegura que el tema no esté ya registrado para evitar duplicados. + if !registered_themes + .iter() + .any(|t| t.type_id() == theme.type_id()) + { + registered_themes.push(theme); + trace::debug!("Enabling \"{}\" theme", theme.short_name()); + } + } else { + trace::debug!("Enabling \"{}\" extension", extension.short_name()); + } } } diff --git a/src/core/extension/definition.rs b/src/core/extension/definition.rs index 625beba..6b963a2 100644 --- a/src/core/extension/definition.rs +++ b/src/core/extension/definition.rs @@ -1,3 +1,4 @@ +use crate::core::theme::ThemeRef; use crate::core::AnyInfo; use crate::locale::L10n; use crate::service; @@ -36,6 +37,31 @@ pub trait ExtensionTrait: AnyInfo + Send + Sync { L10n::default() } + /// Los temas son extensiones que implementan [`ExtensionTrait`] y también + /// [`ThemeTrait`](crate::core::theme::ThemeTrait). + /// + /// Si la extensión no es un tema, este método devuelve `None` por defecto. + /// + /// En caso contrario, este método debe implementarse para devolver una referencia de sí mismo + /// como tema. Por ejemplo: + /// + /// ```rust + /// use pagetop::prelude::*; + /// + /// pub struct MyTheme; + /// + /// impl ExtensionTrait for MyTheme { + /// fn theme(&self) -> Option { + /// Some(&Self) + /// } + /// } + /// + /// impl ThemeTrait for MyTheme {} + /// ``` + fn theme(&self) -> Option { + None + } + /// Otras extensiones que deben habilitarse **antes** de esta. /// /// `PageTop` las resolverá automáticamente respetando el orden durante el arranque de la diff --git a/src/core/theme.rs b/src/core/theme.rs new file mode 100644 index 0000000..d741cf8 --- /dev/null +++ b/src/core/theme.rs @@ -0,0 +1,6 @@ +//! API para añadir y gestionar nuevos temas. + +mod definition; +pub use definition::{ThemeRef, ThemeTrait}; + +pub(crate) mod all; diff --git a/src/core/theme/all.rs b/src/core/theme/all.rs new file mode 100644 index 0000000..3516107 --- /dev/null +++ b/src/core/theme/all.rs @@ -0,0 +1,31 @@ +use crate::core::theme::ThemeRef; +use crate::global; + +use std::sync::{LazyLock, RwLock}; + +// TEMAS ******************************************************************************************* + +pub static THEMES: LazyLock>> = LazyLock::new(|| RwLock::new(Vec::new())); + +// TEMA PREDETERMINADO ***************************************************************************** + +pub static DEFAULT_THEME: LazyLock = + LazyLock::new(|| match theme_by_short_name(&global::SETTINGS.app.theme) { + Some(theme) => theme, + None => &crate::base::theme::Basic, + }); + +// TEMA POR NOMBRE ********************************************************************************* + +pub fn theme_by_short_name(short_name: impl AsRef) -> Option { + let short_name = short_name.as_ref().to_lowercase(); + match THEMES + .read() + .unwrap() + .iter() + .find(|t| t.short_name().to_lowercase() == short_name) + { + Some(theme) => Some(*theme), + _ => None, + } +} diff --git a/src/core/theme/definition.rs b/src/core/theme/definition.rs new file mode 100644 index 0000000..13fe282 --- /dev/null +++ b/src/core/theme/definition.rs @@ -0,0 +1,30 @@ +use crate::core::extension::ExtensionTrait; + +/// Representa una referencia a un tema. +/// +/// Los temas son también extensiones. Por tanto se deben definir igual, es decir, como instancias +/// estáticas globales que implementan [`ThemeTrait`], pero también [`ExtensionTrait`]. +pub type ThemeRef = &'static dyn ThemeTrait; + +/// Interfaz común que debe implementar cualquier tema de `PageTop`. +/// +/// Un tema implementará [`ThemeTrait`] y los métodos que sean necesarios de [`ExtensionTrait`], +/// aunque el único obligatorio es [`theme()`](crate::core::extension::ExtensionTrait#method.theme). +/// +/// ```rust +/// use pagetop::prelude::*; +/// +/// pub struct MyTheme; +/// +/// impl ExtensionTrait for MyTheme { +/// fn name(&self) -> L10n { L10n::n("My theme") } +/// fn description(&self) -> L10n { L10n::n("Un tema personal") } +/// +/// fn theme(&self) -> Option { +/// Some(&Self) +/// } +/// } +/// +/// impl ThemeTrait for MyTheme {} +/// ``` +pub trait ThemeTrait: ExtensionTrait + Send + Sync {} diff --git a/src/global.rs b/src/global.rs index c3332d3..e8f5065 100644 --- a/src/global.rs +++ b/src/global.rs @@ -8,6 +8,7 @@ include_config!(SETTINGS: Settings => [ // [app] "app.name" => "Sample", "app.description" => "Developed with the amazing PageTop framework.", + "app.theme" => "Basic", "app.language" => "en-US", "app.startup_banner" => "Slant", @@ -40,6 +41,8 @@ pub struct App { pub name: String, /// Breve descripción de la aplicación. pub description: String, + /// Tema predeterminado. + pub theme: String, /// Idioma predeterminado (localización). pub language: String, /// Banner ASCII mostrado al inicio: *"Off"* (desactivado), *"Slant"*, *"Small"*, *"Speed"* o diff --git a/src/lib.rs b/src/lib.rs index edc791c..264fdbc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,10 +87,12 @@ pub mod html; pub mod locale; // Soporte a fechas y horas. pub mod datetime; -// Tipos y funciones esenciales para crear extensiones. +// Tipos y funciones esenciales para crear extensiones y temas. pub mod core; // Gestión del servidor y servicios web. pub mod service; +// Reúne temas listos para usar. +pub mod base; // Prepara y ejecuta la aplicación. pub mod app; diff --git a/src/prelude.rs b/src/prelude.rs index 3a9f4f6..7244b91 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -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,9 @@ pub use crate::service; pub use crate::core::{AnyCast, AnyInfo, TypeInfo}; -//pub use crate::core::action::*; pub use crate::core::extension::*; +pub use crate::core::theme::*; + +pub use crate::base::theme; pub use crate::app::Application;