✨ Añade API para extensiones con funcionalidades
Añade el interfaz común que debe implementar cualquier extensión de PageTop para añadir nuevas funcionalidades a la aplicación en forma de servicios web y API de uso.
This commit is contained in:
parent
5d2f293942
commit
0df5f3f1c7
7 changed files with 436 additions and 2 deletions
104
src/core/extension/all.rs
Normal file
104
src/core/extension/all.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
use crate::core::extension::ExtensionRef;
|
||||
use crate::{service, trace};
|
||||
|
||||
use std::sync::{LazyLock, RwLock};
|
||||
|
||||
// EXTENSIONES *************************************************************************************
|
||||
|
||||
static ENABLED_EXTENSIONS: LazyLock<RwLock<Vec<ExtensionRef>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
|
||||
static DROPPED_EXTENSIONS: LazyLock<RwLock<Vec<ExtensionRef>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
|
||||
// REGISTRO DE LAS EXTENSIONES *********************************************************************
|
||||
|
||||
pub fn register_extensions(root_extension: Option<ExtensionRef>) {
|
||||
// Prepara la lista de extensiones habilitadas.
|
||||
let mut enabled_list: Vec<ExtensionRef> = Vec::new();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Guarda la lista final de extensiones habilitadas.
|
||||
ENABLED_EXTENSIONS
|
||||
.write()
|
||||
.unwrap()
|
||||
.append(&mut enabled_list);
|
||||
|
||||
// Prepara una lista de extensiones deshabilitadas.
|
||||
let mut dropped_list: Vec<ExtensionRef> = Vec::new();
|
||||
|
||||
// Si se proporciona una extensión raíz, analiza su lista de dependencias.
|
||||
if let Some(extension) = root_extension {
|
||||
add_to_dropped(&mut dropped_list, extension);
|
||||
}
|
||||
|
||||
// Guarda la lista final de extensiones deshabilitadas.
|
||||
DROPPED_EXTENSIONS
|
||||
.write()
|
||||
.unwrap()
|
||||
.append(&mut dropped_list);
|
||||
}
|
||||
|
||||
fn add_to_enabled(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) {
|
||||
// Verifica que la extensión no esté en la lista para evitar duplicados.
|
||||
if !list.iter().any(|e| e.type_id() == extension.type_id()) {
|
||||
// Añade primero (en orden inverso) las dependencias de la extensión.
|
||||
for d in extension.dependencies().iter().rev() {
|
||||
add_to_enabled(list, *d);
|
||||
}
|
||||
|
||||
// Añade la propia extensión a la lista.
|
||||
list.push(extension);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_to_dropped(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) {
|
||||
// Recorre las extensiones que la actual recomienda deshabilitar.
|
||||
for d in &extension.drop_extensions() {
|
||||
// Verifica que la extensión no esté ya en la lista.
|
||||
if !list.iter().any(|e| e.type_id() == d.type_id()) {
|
||||
// Comprueba si la extensión está habilitada. Si es así, registra una advertencia.
|
||||
if ENABLED_EXTENSIONS
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|e| e.type_id() == extension.type_id())
|
||||
{
|
||||
trace::warn!(
|
||||
"Trying to drop \"{}\" extension which is enabled",
|
||||
extension.short_name()
|
||||
);
|
||||
} else {
|
||||
// Si la extensión no está habilitada, se añade a la lista y registra la acción.
|
||||
list.push(*d);
|
||||
trace::debug!("Extension \"{}\" dropped", d.short_name());
|
||||
// Añade recursivamente las dependencias de la extensión eliminada.
|
||||
// De este modo, todas las dependencias se tienen en cuenta para ser deshabilitadas.
|
||||
for dependency in &extension.dependencies() {
|
||||
add_to_dropped(list, *dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// INICIALIZA LAS EXTENSIONES **********************************************************************
|
||||
|
||||
pub fn initialize_extensions() {
|
||||
trace::info!("Calling application bootstrap");
|
||||
for extension in ENABLED_EXTENSIONS.read().unwrap().iter() {
|
||||
extension.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
// CONFIGURA LOS SERVICIOS *************************************************************************
|
||||
|
||||
pub fn configure_services(scfg: &mut service::web::ServiceConfig) {
|
||||
for extension in ENABLED_EXTENSIONS.read().unwrap().iter() {
|
||||
extension.configure_service(scfg);
|
||||
}
|
||||
}
|
77
src/core/extension/definition.rs
Normal file
77
src/core/extension/definition.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use crate::core::AnyInfo;
|
||||
use crate::locale::L10n;
|
||||
use crate::service;
|
||||
|
||||
/// Representa una referencia a una extensión.
|
||||
///
|
||||
/// Las extensiones se definen como instancias estáticas globales para poder acceder a ellas desde
|
||||
/// cualquier hilo de la ejecución sin necesidad de sincronización adicional.
|
||||
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 la estructura de la extensión y
|
||||
/// sobreescribir los métodos que sea necesario.
|
||||
///
|
||||
/// ```rust
|
||||
/// use pagetop::prelude::*;
|
||||
///
|
||||
/// pub struct Blog;
|
||||
///
|
||||
/// impl ExtensionTrait for Blog {
|
||||
/// fn name(&self) -> L10n { L10n::n("Blog") }
|
||||
/// fn description(&self) -> L10n { L10n::n("Sistema de blogs") }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait ExtensionTrait: AnyInfo + Send + Sync {
|
||||
/// Nombre legible para el usuario.
|
||||
///
|
||||
/// Predeterminado por el [`short_name`](AnyInfo::short_name) del tipo asociado a la extensión.
|
||||
fn name(&self) -> L10n {
|
||||
L10n::n(self.short_name())
|
||||
}
|
||||
|
||||
/// Descripción corta para paneles, listados, etc.
|
||||
fn description(&self) -> L10n {
|
||||
L10n::default()
|
||||
}
|
||||
|
||||
/// Otras extensiones que deben habilitarse **antes** de esta.
|
||||
///
|
||||
/// `PageTop` las resolverá automáticamente respetando el orden durante el arranque de la
|
||||
/// aplicación.
|
||||
fn dependencies(&self) -> Vec<ExtensionRef> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
/// Inicializa la extensión durante la lógica de arranque de la aplicación.
|
||||
///
|
||||
/// Se llama una sola vez, después de que todas las dependencias se han inicializado y antes de
|
||||
/// aceptar cualquier petición HTTP.
|
||||
fn initialize(&self) {}
|
||||
|
||||
/// Configura los servicios web de la extensión, como rutas, *middleware*, acceso a ficheros
|
||||
/// estáticos, etc., usando [`ServiceConfig`](crate::service::web::ServiceConfig).
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use pagetop::prelude::*;
|
||||
///
|
||||
/// pub struct ExtensionSample;
|
||||
///
|
||||
/// impl ExtensionTrait for ExtensionSample {
|
||||
/// fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
|
||||
/// scfg.route("/sample", web::get().to(route_sample));
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused_variables)]
|
||||
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {}
|
||||
|
||||
/// Permite crear extensiones para deshabilitar y desinstalar los recursos de otras extensiones
|
||||
/// utilizadas en versiones anteriores de la aplicación.
|
||||
///
|
||||
/// Actualmente no se usa, pero se deja como *placeholder* para futuras implementaciones.
|
||||
fn drop_extensions(&self) -> Vec<ExtensionRef> {
|
||||
vec![]
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue