✨ 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
37
src/app.rs
37
src/app.rs
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
mod figfont;
|
mod figfont;
|
||||||
|
|
||||||
|
use crate::core::{extension, extension::ExtensionRef};
|
||||||
use crate::{global, locale, service, trace};
|
use crate::{global, locale, service, trace};
|
||||||
|
|
||||||
use substring::Substring;
|
use substring::Substring;
|
||||||
|
@ -9,6 +10,12 @@ use substring::Substring;
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
/// Punto de entrada de una aplicación `PageTop`.
|
||||||
|
///
|
||||||
|
/// No almacena datos, pero **encapsula** el ciclo completo de configuración y puesta en marcha.
|
||||||
|
/// Para instanciarla se puede usar [`new`](Application::new) o [`prepare`](Application::prepare).
|
||||||
|
/// Después sólo hay que llamar a [`run`](Application::run) (o a [`test`](Application::test) si se
|
||||||
|
/// está preparando un entorno de pruebas).
|
||||||
pub struct Application;
|
pub struct Application;
|
||||||
|
|
||||||
impl Default for Application {
|
impl Default for Application {
|
||||||
|
@ -20,6 +27,23 @@ impl Default for Application {
|
||||||
impl Application {
|
impl Application {
|
||||||
/// Crea una instancia de la aplicación.
|
/// Crea una instancia de la aplicación.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
Self::internal_prepare(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepara una instancia de la aplicación a partir de una extensión raíz.
|
||||||
|
///
|
||||||
|
/// Esa extensión suele declarar:
|
||||||
|
///
|
||||||
|
/// - Sus propias dependencias (que se habilitarán automáticamente).
|
||||||
|
/// - Una lista de extensiones que deben deshabilitarse si estuvieran activadas.
|
||||||
|
///
|
||||||
|
/// Esto simplifica el arranque en escenarios complejos.
|
||||||
|
pub fn prepare(root_extension: ExtensionRef) -> Self {
|
||||||
|
Self::internal_prepare(Some(root_extension))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Método interno para preparar la aplicación, opcionalmente con una extensión.
|
||||||
|
fn internal_prepare(root_extension: Option<ExtensionRef>) -> Self {
|
||||||
// Al arrancar muestra una cabecera para la aplicación.
|
// Al arrancar muestra una cabecera para la aplicación.
|
||||||
Self::show_banner();
|
Self::show_banner();
|
||||||
|
|
||||||
|
@ -29,6 +53,12 @@ impl Application {
|
||||||
// Valida el identificador de idioma por defecto.
|
// Valida el identificador de idioma por defecto.
|
||||||
LazyLock::force(&locale::DEFAULT_LANGID);
|
LazyLock::force(&locale::DEFAULT_LANGID);
|
||||||
|
|
||||||
|
// Registra las extensiones de la aplicación.
|
||||||
|
extension::all::register_extensions(root_extension);
|
||||||
|
|
||||||
|
// Inicializa las extensiones.
|
||||||
|
extension::all::initialize_extensions();
|
||||||
|
|
||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +103,10 @@ impl Application {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ejecuta el servidor web de la aplicación.
|
/// Arranca el servidor web de la aplicación.
|
||||||
|
///
|
||||||
|
/// Devuelve [`std::io::Error`] si el *socket* no puede enlazarse (por puerto en uso, permisos,
|
||||||
|
/// etc.).
|
||||||
pub fn run(self) -> Result<service::Server, Error> {
|
pub fn run(self) -> Result<service::Server, Error> {
|
||||||
// Prepara el servidor web.
|
// Prepara el servidor web.
|
||||||
Ok(service::HttpServer::new(move || {
|
Ok(service::HttpServer::new(move || {
|
||||||
|
@ -112,6 +145,6 @@ impl Application {
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
> {
|
> {
|
||||||
service::App::new()
|
service::App::new().configure(extension::all::configure_services)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
205
src/core.rs
Normal file
205
src/core.rs
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
//! Tipos y funciones esenciales para crear extensiones.
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
/// Selector para identificar segmentos de la ruta de un tipo.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum TypeInfo {
|
||||||
|
/// Ruta completa tal y como la devuelve [`core::any::type_name`].
|
||||||
|
FullName,
|
||||||
|
/// Último segmento de la ruta – por ejemplo `Vec<i32>` en lugar de `alloc::vec::Vec<i32>`.
|
||||||
|
ShortName,
|
||||||
|
/// Conserva todo **desde** `start` inclusive hasta el final.
|
||||||
|
NameFrom(isize),
|
||||||
|
/// Conserva todo **hasta e incluyendo** `end`.
|
||||||
|
NameTo(isize),
|
||||||
|
/// Conserva la subruta comprendida entre `start` y `end` (ambos inclusive).
|
||||||
|
PartialName(isize, isize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeInfo {
|
||||||
|
/// Devuelve el segmento solicitado de la ruta para el tipo `T`.
|
||||||
|
pub fn of<T: ?Sized>(&self) -> &'static str {
|
||||||
|
let type_name = std::any::type_name::<T>();
|
||||||
|
match self {
|
||||||
|
TypeInfo::FullName => type_name,
|
||||||
|
TypeInfo::ShortName => Self::partial(type_name, -1, None),
|
||||||
|
TypeInfo::NameFrom(start) => Self::partial(type_name, *start, None),
|
||||||
|
TypeInfo::NameTo(end) => Self::partial(type_name, 0, Some(*end)),
|
||||||
|
TypeInfo::PartialName(start, end) => Self::partial(type_name, *start, Some(*end)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extrae un rango de segmentos de `type_name` (tokens separados por `::`).
|
||||||
|
//
|
||||||
|
// Los argumentos `start` y `end` identifican los índices de los segmentos teniendo en cuenta:
|
||||||
|
//
|
||||||
|
// * Los índices positivos cuentan **desde la izquierda**, empezando en `0`.
|
||||||
|
// * Los índices negativos cuentan **desde la derecha**, `-1` es el último.
|
||||||
|
// * Si `end` es `None`, el corte llega hasta el último segmento.
|
||||||
|
// * Si la selección resulta vacía por índices desordenados o segmento inexistente, se devuelve
|
||||||
|
// la cadena vacía.
|
||||||
|
//
|
||||||
|
// Ejemplos (con `type_name = "alloc::vec::Vec<i32>"`):
|
||||||
|
//
|
||||||
|
// | Llamada | Resultado |
|
||||||
|
// |------------------------------|--------------------------|
|
||||||
|
// | `partial(..., 0, None)` | `"alloc::vec::Vec<i32>"` |
|
||||||
|
// | `partial(..., 1, None)` | `"vec::Vec<i32>"` |
|
||||||
|
// | `partial(..., -1, None)` | `"Vec<i32>"` |
|
||||||
|
// | `partial(..., 0, Some(-2))` | `"alloc::vec"` |
|
||||||
|
// | `partial(..., -5, None)` | `"alloc::vec::Vec<i32>"` |
|
||||||
|
//
|
||||||
|
// La porción devuelta vive tanto como `'static` porque `type_name` es `'static` y sólo se
|
||||||
|
// presta.
|
||||||
|
fn partial(type_name: &'static str, start: isize, end: Option<isize>) -> &'static str {
|
||||||
|
let maxlen = type_name.len();
|
||||||
|
|
||||||
|
// Localiza los límites de cada segmento a nivel 0 de `<…>`.
|
||||||
|
let mut segments = Vec::new();
|
||||||
|
let mut segment_start = 0; // Posición inicial del segmento actual.
|
||||||
|
let mut angle_brackets = 0; // Profundidad dentro de '<…>'.
|
||||||
|
let mut previous_char = '\0'; // Se inicializa a carácter nulo, no hay aún carácter previo.
|
||||||
|
|
||||||
|
for (idx, c) in type_name.char_indices() {
|
||||||
|
match c {
|
||||||
|
':' if angle_brackets == 0 => {
|
||||||
|
if previous_char == ':' {
|
||||||
|
if segment_start < idx - 1 {
|
||||||
|
segments.push((segment_start, idx - 1)); // No incluye último '::'.
|
||||||
|
}
|
||||||
|
segment_start = idx + 1; // Nuevo segmento tras '::'.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'<' => angle_brackets += 1,
|
||||||
|
'>' => angle_brackets -= 1,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
previous_char = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incluye el último segmento si lo hubiese.
|
||||||
|
if segment_start < maxlen {
|
||||||
|
segments.push((segment_start, maxlen));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcula la posición inicial.
|
||||||
|
let start_pos = segments
|
||||||
|
.get(if start >= 0 {
|
||||||
|
start as usize
|
||||||
|
} else {
|
||||||
|
segments.len().saturating_sub(start.unsigned_abs())
|
||||||
|
})
|
||||||
|
.map_or(0, |&(s, _)| s);
|
||||||
|
|
||||||
|
// Calcula la posición final.
|
||||||
|
let end_pos = segments
|
||||||
|
.get(if let Some(end) = end {
|
||||||
|
if end >= 0 {
|
||||||
|
end as usize
|
||||||
|
} else {
|
||||||
|
segments.len().saturating_sub(end.unsigned_abs())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
segments.len() - 1
|
||||||
|
})
|
||||||
|
.map_or(maxlen, |&(_, e)| e);
|
||||||
|
|
||||||
|
// Devuelve la cadena parcial basada en las posiciones calculadas.
|
||||||
|
if start_pos >= end_pos {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
&type_name[start_pos..end_pos]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Proporciona información de tipo en tiempo de ejecución y conversión dinámica de tipos.
|
||||||
|
///
|
||||||
|
/// Este *trait* se implementa automáticamente para **todos** los tipos que implementen [`Any`], de
|
||||||
|
/// modo que basta con traer [`AnyInfo`] al ámbito (`use crate::AnyInfo;`) para disponer de estos
|
||||||
|
/// métodos adicionales, o usar el [`prelude`](crate::prelude) de `PageTop`.
|
||||||
|
///
|
||||||
|
/// # Ejemplo
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use pagetop::prelude::*;
|
||||||
|
///
|
||||||
|
/// let n = 3u32;
|
||||||
|
/// assert_eq!(n.type_name(), "u32");
|
||||||
|
/// ```
|
||||||
|
pub trait AnyInfo: Any {
|
||||||
|
/// Devuelve el nombre totalmente cualificado del tipo.
|
||||||
|
fn type_name(&self) -> &'static str;
|
||||||
|
|
||||||
|
/// Devuelve el nombre corto del tipo (último segmento del nombre).
|
||||||
|
fn short_name(&self) -> &'static str;
|
||||||
|
|
||||||
|
/// Devuelve una referencia a `dyn Any` para la conversión dinámica de tipos.
|
||||||
|
fn as_any_ref(&self) -> &dyn Any;
|
||||||
|
|
||||||
|
/// Devuelve una referencia mutable a `dyn Any` para la conversión dinámica de tipos.
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Any> AnyInfo for T {
|
||||||
|
#[inline]
|
||||||
|
fn type_name(&self) -> &'static str {
|
||||||
|
TypeInfo::FullName.of::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
TypeInfo::ShortName.of::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_any_ref(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extiende [`AnyInfo`] con utilidades de *downcasting* para conversión de tipos.
|
||||||
|
///
|
||||||
|
/// Preferible a usar directamente `Any::downcast_ref` porque conserva el *trait bound* [`AnyInfo`],
|
||||||
|
/// lo que permite seguir llamando a `type_name`, etc.
|
||||||
|
pub trait AnyCast: AnyInfo {
|
||||||
|
/// Comprueba si la instancia subyacente es de tipo `T`.
|
||||||
|
#[inline]
|
||||||
|
fn is<T>(&self) -> bool
|
||||||
|
where
|
||||||
|
T: AnyInfo,
|
||||||
|
{
|
||||||
|
self.as_any_ref().is::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intenta hacer *downcast* de un objeto para obtener una referencia de tipo `T`.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn downcast_ref<T>(&self) -> Option<&T>
|
||||||
|
where
|
||||||
|
T: AnyInfo,
|
||||||
|
{
|
||||||
|
self.as_any_ref().downcast_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intenta hacer *downcast* de un objeto para obtener una referencia mutable de tipo `T`.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn downcast_mut<T>(&mut self) -> Option<&mut T>
|
||||||
|
where
|
||||||
|
T: AnyInfo,
|
||||||
|
{
|
||||||
|
self.as_any_mut().downcast_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementación automática para cualquier tipo que ya cumpla [`AnyInfo`].
|
||||||
|
impl<T: ?Sized + AnyInfo> AnyCast for T {}
|
||||||
|
|
||||||
|
// Infraestructura para ampliar funcionalidades mediante extensiones.
|
||||||
|
pub mod extension;
|
9
src/core/extension.rs
Normal file
9
src/core/extension.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//! 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`].
|
||||||
|
|
||||||
|
mod definition;
|
||||||
|
pub use definition::{ExtensionRef, ExtensionTrait};
|
||||||
|
|
||||||
|
pub(crate) mod all;
|
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![]
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,6 +74,8 @@ pub mod html;
|
||||||
pub mod locale;
|
pub mod locale;
|
||||||
// Soporte a fechas y horas.
|
// Soporte a fechas y horas.
|
||||||
pub mod datetime;
|
pub mod datetime;
|
||||||
|
// Tipos y funciones esenciales para crear extensiones.
|
||||||
|
pub mod core;
|
||||||
// Gestión del servidor y servicios web.
|
// Gestión del servidor y servicios web.
|
||||||
pub mod service;
|
pub mod service;
|
||||||
// Prepara y ejecuta la aplicación.
|
// Prepara y ejecuta la aplicación.
|
||||||
|
|
|
@ -33,4 +33,8 @@ pub use crate::datetime::*;
|
||||||
|
|
||||||
pub use crate::service;
|
pub use crate::service;
|
||||||
|
|
||||||
|
pub use crate::core::{AnyCast, AnyInfo, TypeInfo};
|
||||||
|
|
||||||
|
pub use crate::core::extension::*;
|
||||||
|
|
||||||
pub use crate::app::Application;
|
pub use crate::app::Application;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue