Añade carga de ajustes de configuración globales

This commit is contained in:
Manuel Cillero 2022-02-12 10:14:36 +01:00
parent 4e23523e80
commit 96884cbbc0
12 changed files with 146 additions and 12 deletions

View file

@ -24,15 +24,18 @@ categories = [
]
[dependencies]
doc-comment = "0.3.3"
once_cell = "1.9.0"
config_rs = { package = "config", version = "0.11.0", features = ["toml"] }
actix-web = "4.0.0-rc.3"
sycamore = { version = "0.8.0-beta.2", features = ["ssr"] }
tokio = { version = "1.16", features = ["macros", "rt-multi-thread"] }
serde = { version = "1.0", features = ["derive"] }
[lib]
name = "pagetop"
path = "src/lib.rs"
[[bin]]
name = "pagetop"
path = "src/main.rs"

View file

@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
pagetop = { path = "pagetop" }
actix-web = "4.0.0-rc.3"
tokio = { version = "1.16", features = ["macros", "rt-multi-thread"] }
[[bin]]
name = "app"

2
config/default.toml Normal file
View file

@ -0,0 +1,2 @@
[app]
name = "PageTop Essence"

View file

@ -0,0 +1,9 @@
[app]
name = "PageTop Application"
description = "Developed with the amazing PageTop framework."
[webserver]
# Configuración opcional del servidor web.
# Usar bind_address = "" para deshabilitar el servidor web.
bind_address = "localhost"
bind_port = 8088

5
src/config/mod.rs Normal file
View file

@ -0,0 +1,5 @@
/// Nombre del directorio donde se encuentra la configuración.
pub const CONFIG_DIR: &'static str = "config";
mod settings;
pub use crate::config::settings::{CONFIG, SETTINGS};

103
src/config/settings.rs Normal file
View file

@ -0,0 +1,103 @@
use crate::Lazy;
use crate::config::CONFIG_DIR;
use config_rs::{Config, File};
use serde::Deserialize;
use std::env;
/// Carga los ajustes globales "clave = valor" al arrancar la aplicación.
pub static CONFIG: Lazy<Config> = Lazy::new(|| {
// Establece el modo de ejecución según el valor de la variable de entorno
// PAGETOP_RUN_MODE. Asume "default" por defecto.
let run_mode = env::var("PAGETOP_RUN_MODE").unwrap_or("default".into());
// Inicializa los ajustes.
let mut settings = Config::default();
// Lee los ajustes combinando los archivos de configuración disponibles y
// asigna el modo de ejecución.
settings
.merge(
File::with_name(
&format!("{}/{}.toml", CONFIG_DIR, "common")
).required(false)).unwrap()
.merge(
File::with_name(
&format!("{}/{}.toml", CONFIG_DIR, run_mode)
).required(false)).unwrap()
.merge(
File::with_name(
&format!("{}/{}.toml", CONFIG_DIR, "local")
).required(false)).unwrap()
.set("app.run_mode", run_mode).unwrap();
settings
});
#[macro_export]
/// Usar esta macro para obtener el valor de cualquier ajuste global, donde
/// clave y valor son cadenas de caracteres. Devuelve la cadena vacía si no
/// encuentra un ajuste para la clave.
macro_rules! config_get {
( $key:expr ) => {
$crate::config::CONFIG.get_str($key).unwrap_or("".to_string())
};
}
#[macro_export]
/// Carga los ajustes específicos de tu módulo o aplicación en una estructura
/// similar a [`SETTINGS`] con tipos de variables seguros. Genera un *panic!*
/// en caso de asignaciones no válidas.
macro_rules! config_map {
( $COMMENT:expr, $CONF:ident, $TYPE:tt $(, $key:expr => $value:expr)* ) => {
$crate::doc_comment! {
concat!($COMMENT),
pub static $CONF: $crate::Lazy<$TYPE> = $crate::Lazy::new(|| {
let mut settings = $crate::config::CONFIG.clone();
$(
settings.set_default($key, $value).unwrap();
)*
match settings.try_into() {
Ok(c) => c,
Err(e) => panic!("Error parsing settings: {}", e),
}
});
}
};
}
#[derive(Debug, Deserialize)]
pub struct App {
pub name : String,
pub description : String,
pub run_mode : String,
}
#[derive(Debug, Deserialize)]
pub struct Webserver {
pub bind_address : String,
pub bind_port : u16,
}
#[derive(Debug, Deserialize)]
pub struct Settings {
pub app : App,
pub webserver : Webserver,
}
config_map!(r#"
Ajustes globales y valores por defecto para las secciones *\[app\]* y
*\[webserver\]* específicas de PageTop.
"#,
SETTINGS, Settings,
// [app]
"app.name" => "PageTop Application",
"app.description" => "Developed with the amazing PageTop framework.",
// [webserver]
"webserver.bind_address" => "localhost",
"webserver.bind_port" => 8088
);

View file

@ -1,6 +1,6 @@
use crate::core::server;
/// Modules must implement this trait.
/// Los módulos deben implementar este *trait*.
pub trait Module: Send + Sync {
fn name(&self) -> String;

View file

@ -1,16 +1,21 @@
use crate::config::SETTINGS;
use crate::core::{Server, all, server};
pub fn run(bootstrap: Option<fn()>) -> Result<Server, std::io::Error> {
// Call application bootstrap.
// Ejecuta la función de inicio específica para la aplicación.
if bootstrap != None {
let _ = &(bootstrap.unwrap())();
}
// Inicializa el servidor web.
let server = server::HttpServer::new(|| {
server::App::new()
.configure(&all::modules)
})
.bind("127.0.0.1:8000")?
.bind(format!("{}:{}",
&SETTINGS.webserver.bind_address,
&SETTINGS.webserver.bind_port
))?
.run();
Ok(server)
}

View file

@ -4,7 +4,7 @@ use crate::core::module::Module;
use std::sync::RwLock;
// -----------------------------------------------------------------------------
// Registered modules.
// Módulos registrados.
// -----------------------------------------------------------------------------
pub static MODULES: Lazy<RwLock<Vec<&dyn Module>>> = Lazy::new(|| {

View file

@ -1,7 +1,11 @@
// Global.
pub use doc_comment::doc_comment;
pub use once_cell::sync::Lazy;
pub mod core;
// -----------------------------------------------------------------------------
// APIs públicas.
// -----------------------------------------------------------------------------
pub use actix_web::main;
pub mod config; // Gestión de la configuración.
pub mod core; // Servidor web y sistemas para Temas, Módulos y Respuestas.

View file

@ -1,3 +1,4 @@
use pagetop::config_get;
use pagetop::core::module::Module;
use pagetop::core::{register_module, server};
@ -16,7 +17,7 @@ impl Module for Greet {
}
async fn greet() -> impl server::Responder {
"Hello!"
format!("Hello from {}!", config_get!("app.name"))
}
struct GreetWithParam;
@ -46,7 +47,7 @@ fn bootstrap() {
register_module(&GreetWithParam);
}
#[pagetop::main]
#[tokio::main]
async fn main() -> std::io::Result<()> {
server::run(Some(bootstrap))?.await
}

View file

@ -1,5 +1,7 @@
use pagetop::core::server;
fn spawn_app() {
let server = pagetop::core::server::run().expect("Failed to bind address");
let server = server::run(None).expect("Failed to bind address");
let _ = tokio::spawn(server);
}