🧑‍💻 Simplifica uso de archivos de configuración

This commit is contained in:
Manuel Cillero 2022-10-18 21:22:28 +02:00
parent df6eaf19cc
commit 68af9caef0
16 changed files with 216 additions and 202 deletions

View file

@ -58,7 +58,7 @@ fn form_login() -> Form {
t( t(
"username_help", "username_help",
&args![ &args![
"app" => SETTINGS.app.name.to_owned() "app" => config::get("app.name").to_owned()
], ],
) )
.as_str(), .as_str(),

View file

@ -24,7 +24,6 @@ categories = [
[dependencies] [dependencies]
async-trait = "0.1.57" async-trait = "0.1.57"
concat-string = "1.0.1" concat-string = "1.0.1"
doc-comment = "0.3.3"
figlet-rs = "0.1.4" figlet-rs = "0.1.4"
futures = "0.3.24" futures = "0.3.24"
once_cell = "1.15.0" once_cell = "1.15.0"
@ -33,7 +32,7 @@ substring = "1.4.5"
term_size = "0.3.2" term_size = "0.3.2"
url = "2.3.1" url = "2.3.1"
config_rs = { package = "config", version = "0.11.0", features = ["toml"] } config_rs = { package = "config", version = "0.13.2", features = ["toml"] }
tracing = "0.1.36" tracing = "0.1.36"
tracing-appender = "0.2.2" tracing-appender = "0.2.2"
@ -51,8 +50,6 @@ static-files = "0.2.3"
maud = { version = "0.24.0", features = ["actix-web"] } maud = { version = "0.24.0", features = ["actix-web"] }
serde = { version = "1.0", features = ["derive"] }
[dependencies.sea-orm] [dependencies.sea-orm]
version = "0.9.3" version = "0.9.3"
features = ["debug-print", "macros", "runtime-async-std-native-tls"] features = ["debug-print", "macros", "runtime-async-std-native-tls"]

View file

@ -1,5 +1,5 @@
use super::fatal_error::FatalError; use super::fatal_error::FatalError;
use crate::config::SETTINGS; use crate::config;
use crate::core::module::ModuleStaticRef; use crate::core::module::ModuleStaticRef;
use crate::core::{module, theme}; use crate::core::{module, theme};
use crate::html::Markup; use crate::html::Markup;
@ -55,7 +55,8 @@ impl Application {
}) })
.bind(format!( .bind(format!(
"{}:{}", "{}:{}",
&SETTINGS.webserver.bind_address, &SETTINGS.webserver.bind_port config::get("webserver.bind_address"),
config::get("webserver.bind_port")
))? ))?
.run(); .run();

View file

@ -1,23 +1,23 @@
mod figfont; mod figfont;
use figfont::FIGFONT; use figfont::FIGFONT;
use crate::config::SETTINGS; use crate::config;
use substring::Substring; use substring::Substring;
pub fn print_on_startup() { pub fn print_on_startup() {
if SETTINGS.app.startup_banner.to_lowercase() != "off" { if config::get("app.startup_banner").to_lowercase() != "off" {
if let Some((term_width, _)) = term_size::dimensions() { if let Some((term_width, _)) = term_size::dimensions() {
if term_width >= 80 { if term_width >= 80 {
let maxlen = (term_width / 10) - 2; let maxlen = (term_width / 10) - 2;
let mut app = SETTINGS.app.name.substring(0, maxlen).to_owned(); let mut app = config::get("app.name").substring(0, maxlen).to_owned();
if SETTINGS.app.name.len() > maxlen { if config::get("app.name").len() > maxlen {
app = format!("{}...", app); app = format!("{}...", app);
} }
println!( println!(
"\n{} {}\n\n Powered by PageTop {}\n", "\n{} {}\n\n Powered by PageTop {}\n",
FIGFONT.convert(&app).unwrap(), FIGFONT.convert(&app).unwrap(),
&SETTINGS.app.description, config::get("app.description"),
env!("CARGO_PKG_VERSION") env!("CARGO_PKG_VERSION")
); );
return; return;
@ -25,8 +25,8 @@ pub fn print_on_startup() {
} }
println!( println!(
"\n{}\n{}\n\nPowered by PageTop {}\n", "\n{}\n{}\n\nPowered by PageTop {}\n",
&SETTINGS.app.name, config::get("app.name"),
&SETTINGS.app.description, config::get("app.description"),
env!("CARGO_PKG_VERSION") env!("CARGO_PKG_VERSION")
); );
} }

View file

@ -1,5 +1,4 @@
use crate::config::SETTINGS; use crate::{config, LazyStatic};
use crate::LazyStatic;
use figlet_rs::FIGfont; use figlet_rs::FIGfont;
@ -9,7 +8,7 @@ pub static FIGFONT: LazyStatic<FIGfont> = LazyStatic::new(|| {
let speed = include_str!("speed.flf"); let speed = include_str!("speed.flf");
let starwars = include_str!("starwars.flf"); let starwars = include_str!("starwars.flf");
FIGfont::from_content(match SETTINGS.app.startup_banner.to_lowercase().as_str() { FIGfont::from_content(match config::get("app.startup_banner").to_lowercase().as_str() {
"off" => slant, "off" => slant,
"slant" => slant, "slant" => slant,
"small" => small, "small" => small,
@ -18,7 +17,7 @@ pub static FIGFONT: LazyStatic<FIGfont> = LazyStatic::new(|| {
_ => { _ => {
println!( println!(
"\n FIGfont \"{}\" not found for banner. Using \"Slant\". Check the settings file.", "\n FIGfont \"{}\" not found for banner. Using \"Slant\". Check the settings file.",
SETTINGS.app.startup_banner, config::get("app.startup_banner"),
); );
slant slant
} }

View file

@ -1,4 +1,4 @@
use crate::config::SETTINGS; use crate::config;
use crate::db::*; use crate::db::*;
use crate::{run_now, trace, LazyStatic}; use crate::{run_now, trace, LazyStatic};
@ -8,38 +8,38 @@ use tracing_unwrap::ResultExt;
pub static DBCONN: LazyStatic<DbConn> = LazyStatic::new(|| { pub static DBCONN: LazyStatic<DbConn> = LazyStatic::new(|| {
trace::info!( trace::info!(
"Connecting to database \"{}\" using a pool of {} connections", "Connecting to database \"{}\" using a pool of {} connections",
&SETTINGS.database.db_name, config::get("database.db_name"),
&SETTINGS.database.max_pool_size config::get("database.max_pool_size")
); );
let db_uri = match SETTINGS.database.db_type.as_str() { let db_uri = match config::get("database.db_type").as_str() {
"mysql" | "postgres" => { "mysql" | "postgres" => {
let mut tmp_uri = DbUri::parse( let mut tmp_uri = DbUri::parse(
format!( format!(
"{}://{}/{}", "{}://{}/{}",
&SETTINGS.database.db_type, config::get("database.db_type"),
&SETTINGS.database.db_host, config::get("database.db_host"),
&SETTINGS.database.db_name config::get("database.db_name")
) )
.as_str(), .as_str(),
) )
.unwrap(); .unwrap();
tmp_uri tmp_uri
.set_username(SETTINGS.database.db_user.as_str()) .set_username(config::get("database.db_user").as_str())
.unwrap(); .unwrap();
// https://github.com/launchbadge/sqlx/issues/1624 // https://github.com/launchbadge/sqlx/issues/1624
tmp_uri tmp_uri
.set_password(Some(SETTINGS.database.db_pass.as_str())) .set_password(Some(config::get("database.db_pass").as_str()))
.unwrap(); .unwrap();
if SETTINGS.database.db_port != 0 { if config::get_value::<u16>("database.db_port") != 0 {
tmp_uri.set_port(Some(SETTINGS.database.db_port)).unwrap(); tmp_uri.set_port(Some(config::get_value::<u16>("database.db_port"))).unwrap();
} }
tmp_uri tmp_uri
} }
"sqlite" => DbUri::parse( "sqlite" => DbUri::parse(
format!( format!(
"{}://{}", "{}://{}",
&SETTINGS.database.db_type, &SETTINGS.database.db_name config::get("database.db_type"), &config::get("database.db_name")
) )
.as_str(), .as_str(),
) )
@ -47,7 +47,7 @@ pub static DBCONN: LazyStatic<DbConn> = LazyStatic::new(|| {
_ => { _ => {
trace::error!( trace::error!(
"Unrecognized database type \"{}\"", "Unrecognized database type \"{}\"",
&SETTINGS.database.db_type config::get("database.db_type")
); );
DbUri::parse("").unwrap() DbUri::parse("").unwrap()
} }
@ -55,7 +55,7 @@ pub static DBCONN: LazyStatic<DbConn> = LazyStatic::new(|| {
run_now(Database::connect::<ConnectOptions>({ run_now(Database::connect::<ConnectOptions>({
let mut db_opt = ConnectOptions::new(db_uri.to_string()); let mut db_opt = ConnectOptions::new(db_uri.to_string());
db_opt.max_connections(SETTINGS.database.max_pool_size); db_opt.max_connections(config::get_value::<u32>("database.max_pool_size"));
db_opt db_opt
})) }))
.expect_or_log("Failed to connect to database") .expect_or_log("Failed to connect to database")

View file

@ -1,5 +1,4 @@
use crate::config::SETTINGS; use crate::{config, trace, LazyStatic};
use crate::{trace, LazyStatic};
use unic_langid::LanguageIdentifier; use unic_langid::LanguageIdentifier;
@ -7,14 +6,14 @@ use unic_langid::LanguageIdentifier;
/// (https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) de /// (https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) de
/// la aplicación, obtenido de `SETTINGS.app.language`. /// la aplicación, obtenido de `SETTINGS.app.language`.
pub static LANGID: LazyStatic<LanguageIdentifier> = pub static LANGID: LazyStatic<LanguageIdentifier> =
LazyStatic::new(|| match SETTINGS.app.language.parse() { LazyStatic::new(|| match config::get("app.language").parse() {
Ok(language) => language, Ok(language) => language,
Err(_) => { Err(_) => {
trace::warn!( trace::warn!(
"{}, {} \"{}\"! {}, {}", "{}, {} \"{}\"! {}, {}",
"Failed to parse language", "Failed to parse language",
"unrecognized Unicode Language Identifier", "unrecognized Unicode Language Identifier",
SETTINGS.app.language, config::get("app.language"),
"Using \"en-US\"", "Using \"en-US\"",
"check the settings file", "check the settings file",
); );

View file

@ -1,5 +1,4 @@
use crate::config::SETTINGS; use crate::{config, LazyStatic};
use crate::LazyStatic;
use tracing_appender::non_blocking::WorkerGuard; use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
@ -20,14 +19,14 @@ use tracing_subscriber::EnvFilter;
#[rustfmt::skip] #[rustfmt::skip]
pub static TRACING: LazyStatic<WorkerGuard> = LazyStatic::new(|| { pub static TRACING: LazyStatic<WorkerGuard> = LazyStatic::new(|| {
let env_filter = let env_filter =
EnvFilter::try_new(&SETTINGS.log.tracing).unwrap_or_else(|_| EnvFilter::new("Info")); EnvFilter::try_new(config::get("log.tracing")).unwrap_or_else(|_| EnvFilter::new("Info"));
let rolling = SETTINGS.log.rolling.to_lowercase(); let rolling = config::get("log.rolling").to_lowercase();
let (non_blocking, guard) = match rolling.as_str() { let (non_blocking, guard) = match rolling.as_str() {
"stdout" => tracing_appender::non_blocking(std::io::stdout()), "stdout" => tracing_appender::non_blocking(std::io::stdout()),
_ => tracing_appender::non_blocking({ _ => tracing_appender::non_blocking({
let path = &SETTINGS.log.path; let path = config::get("log.path");
let prefix = &SETTINGS.log.prefix; let prefix = config::get("log.prefix");
match rolling.as_str() { match rolling.as_str() {
"daily" => tracing_appender::rolling::daily(path, prefix), "daily" => tracing_appender::rolling::daily(path, prefix),
"hourly" => tracing_appender::rolling::hourly(path, prefix), "hourly" => tracing_appender::rolling::hourly(path, prefix),
@ -36,7 +35,7 @@ pub static TRACING: LazyStatic<WorkerGuard> = LazyStatic::new(|| {
_ => { _ => {
println!( println!(
"Rolling value \"{}\" not valid. Using \"daily\". Check the settings file.", "Rolling value \"{}\" not valid. Using \"daily\". Check the settings file.",
SETTINGS.log.rolling, config::get("log.rolling"),
); );
tracing_appender::rolling::daily(path, prefix) tracing_appender::rolling::daily(path, prefix)
} }
@ -47,7 +46,7 @@ pub static TRACING: LazyStatic<WorkerGuard> = LazyStatic::new(|| {
.with_env_filter(env_filter) .with_env_filter(env_filter)
.with_writer(non_blocking) .with_writer(non_blocking)
.with_ansi(rolling.as_str() == "stdout"); .with_ansi(rolling.as_str() == "stdout");
match SETTINGS.log.format.to_lowercase().as_str() { match config::get("log.format").to_lowercase().as_str() {
"json" => subscriber.json().init(), "json" => subscriber.json().init(),
"full" => subscriber.init(), "full" => subscriber.init(),
"compact" => subscriber.compact().init(), "compact" => subscriber.compact().init(),
@ -55,7 +54,7 @@ pub static TRACING: LazyStatic<WorkerGuard> = LazyStatic::new(|| {
_ => { _ => {
println!( println!(
"Tracing format \"{}\" not valid. Using \"Full\". Check the settings file.", "Tracing format \"{}\" not valid. Using \"Full\". Check the settings file.",
SETTINGS.log.format, config::get("log.format"),
); );
subscriber.init(); subscriber.init();
} }

View file

@ -55,7 +55,7 @@ fn hello_world() -> Container {
.with_component( .with_component(
Paragraph::with(html! { Paragraph::with(html! {
(e("hello_intro", &args![ (e("hello_intro", &args![
"app" => format!("<span class=\"app-name\">{}</span>", &SETTINGS.app.name) "app" => format!("<span class=\"app-name\">{}</span>", config::get("app.name"))
])) ]))
}) })
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
@ -101,7 +101,7 @@ fn welcome() -> Container {
.with_component( .with_component(
Heading::h3(html! { Heading::h3(html! {
(e("welcome_subtitle", &args![ (e("welcome_subtitle", &args![
"app" => format!("<span class=\"app-name\">{}</span>", &SETTINGS.app.name) "app" => format!("<span class=\"app-name\">{}</span>", config::get("app.name"))
])) ]))
}) })
.with_display(HeadingDisplay::Subtitle), .with_display(HeadingDisplay::Subtitle),

View file

@ -1,158 +1,139 @@
//! Gestión de la configuración.
//!
//! Comprueba el modo de ejecución actual y carga la configuración asociada.
//!
//! PageTop aplica los principios de [The Twelve-Factor App](https://12factor.net/es/) cargando
//! archivos de configuración [TOML](https://toml.io) con pares `clave = valor` que pueden diferir
//! según el modo de ejecución del entorno actual, o al migrar a otros entornos (*desarrollo*,
//! *pre-producción*, *producción*, etc.).
//!
//! # ¿Cómo usar archivos de configuración?
//!
//! Si tu aplicación requiere opciones de configuración, primero debes crear un directorio llamado
//! *config* (ver [`CONFIG_DIR`]) al mismo nivel del archivo *Cargo.toml* de tu proyecto (o del
//! archivo binario ejecutable de la aplicación).
//!
//! Luego guarda la configuración usando archivos TOML asumiendo el siguiente orden de lectura
//! (todos los archivos son opcionales):
//!
//! 1. *config/common.toml*, útil para asignar valores comunes a cualquier entorno. Estos valores
//! pueden ser modificados al fusionar los siguientes archivos de configuración.
//!
//! 2. *config/{archivo}.toml*, donde *{archivo}* puede definirse mediante la variable de entorno
//! PAGETOP_RUN_MODE:
//!
//! * Si no está definido, se asumirá *default* como nombre predeterminado y PageTop cargará el
//! archivo de configuración *config/default.toml* si existe.
//!
//! * De esta manera, se podrían tener diferentes opciones de configuración para diferentes
//! entornos de ejecución. Por ejemplo, para *devel.toml*, *staging.toml* o *production.toml*.
//! O también para *server1.toml* o *server2.toml*. Sólo uno será cargado.
//!
//! 3. *config/local.toml*, para añadir o sobrescribir ajustes.
use crate::LazyStatic; use crate::LazyStatic;
use config_rs::{Config, File}; use config_rs::{Config, File};
use serde::Deserialize;
use std::collections::HashMap;
use std::default::Default;
use std::env; use std::env;
use std::fmt::Debug;
use std::str::FromStr;
use std::sync::RwLock;
/// Nombre del directorio donde se encuentra la configuración. #[macro_export]
const CONFIG_DIR: &str = "config"; macro_rules! default_settings {
( $($key:literal => $value:literal),* ) => {{
let mut a = std::collections::HashMap::new();
$(
a.insert($key, $value);
)*
a
}};
}
/// Al arrancar la aplicación, carga los valores originales "clave = valor" de /// Directorio donde se encuentran los archivos de configuración.
/// los archivos de configuración. Con [`config_map`] se asignarán los ajustes pub const CONFIG_DIR: &str = "config";
/// globales ([`SETTINGS`]); y se podrán asignar los ajustes específicos de la
/// aplicación, o también de un tema, módulo o componente. /// Carga los valores originales "clave = valor" de los archivos de configuración. Con
pub static CONFIG: LazyStatic<Config> = LazyStatic::new(|| { /// [`config_map`] se asignarán los ajustes globales ([`SETTINGS`]); y se podrán asignar los ajustes
// Establece el modo de ejecución según el valor de la variable de entorno /// específicos de la aplicación, o también de un tema, módulo o componente.
// PAGETOP_RUN_MODE. Asume "default" por defecto. static CONFIG: LazyStatic<Config> = LazyStatic::new(|| {
// Modo de ejecución según la variable de entorno PAGETOP_RUN_MODE. Por defecto 'default'.
let run_mode = env::var("PAGETOP_RUN_MODE").unwrap_or_else(|_| "default".into()); let run_mode = env::var("PAGETOP_RUN_MODE").unwrap_or_else(|_| "default".into());
// Inicializa los ajustes. // Inicializa los ajustes.
let mut settings = Config::default(); let settings = Config::builder();
// Combina los archivos de configuración y asigna el modo de ejecución. // Combina los archivos de configuración y asigna el modo de ejecución.
settings settings
.merge(File::with_name(&format!("{}/{}.toml", CONFIG_DIR, "common")).required(false)) // Primero añade configuración común a todos los entornos. Opcional.
.unwrap() .add_source(File::with_name(&format!("{}/{}.toml", CONFIG_DIR, "common")).required(false))
.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 // Combina la configuración específica del entorno. Por defecto 'default.toml'. Opcional.
.add_source(File::with_name(&format!("{}/{}.toml", CONFIG_DIR, run_mode)).required(false))
// Combina la configuración local. Este archivo no debería incluirse en git. Opcional.
.add_source(File::with_name(&format!("{}/{}.toml", CONFIG_DIR, "local")).required(false))
// Salvaguarda el modo de ejecución.
.set_default("app.run_mode", run_mode)
.unwrap()
.build()
.unwrap()
}); });
#[macro_export] static DEFAULTS: LazyStatic<RwLock<HashMap<&str, &str>>> = LazyStatic::new(||
/// Asigna los ajustes específicos de la aplicación, o de un tema, módulo o RwLock::new(default_settings![
/// componente, en una estructura similar a [`SETTINGS`] con tipos de variables // [app]
/// seguros. Produce un *panic!* en caso de asignaciones no válidas. "app.name" => "PageTop Application",
macro_rules! config_map { "app.description" => "Developed with the amazing PageTop framework.",
( "app.theme" => "Bootsier",
$doc:expr, "app.language" => "en-US",
$SETTINGS:ident, "app.direction" => "ltr",
$Type:tt "app.startup_banner" => "Slant",
$(, $key:expr => $value:expr)*
) => {
$crate::doc_comment! {
concat!($doc),
pub static $SETTINGS: $crate::LazyStatic<$Type> = $crate::LazyStatic::new(|| { // [log]
let mut settings = $crate::config::CONFIG.clone(); "log.tracing" => "Info",
$( "log.rolling" => "Stdout",
settings.set_default($key, $value).unwrap(); "log.path" => "log",
)* "log.prefix" => "tracing.log",
match settings.try_into() { "log.format" => "Full",
Ok(c) => c,
Err(e) => panic!("Error parsing settings: {}", e),
}
});
}
};
}
#[rustfmt::skip] // [database]
#[derive(Debug, Deserialize)] "database.db_type" => "",
pub struct App { "database.db_name" => "",
pub name : String, "database.db_user" => "",
pub description : String, "database.db_pass" => "",
pub theme : String, "database.db_host" => "localhost",
pub language : String, "database.db_port" => "0",
pub direction : String, "database.max_pool_size" => "5",
pub startup_banner: String,
pub run_mode : String,
}
#[rustfmt::skip] // [webserver]
#[derive(Debug, Deserialize)] "webserver.bind_address" => "localhost",
pub struct Log { "webserver.bind_port" => "8088",
pub tracing : String,
pub rolling : String,
pub path : String,
pub prefix : String,
pub format : String,
}
#[rustfmt::skip] // [dev]
#[derive(Debug, Deserialize)] "dev.static_files" => ""
pub struct Database { ])
pub db_type : String,
pub db_name : String,
pub db_user : String,
pub db_pass : String,
pub db_host : String,
pub db_port : u16,
pub max_pool_size : u32,
}
#[rustfmt::skip]
#[derive(Debug, Deserialize)]
pub struct Webserver {
pub bind_address : String,
pub bind_port : u16,
}
#[rustfmt::skip]
#[derive(Debug, Deserialize)]
pub struct Dev {
pub static_files : String,
}
#[rustfmt::skip]
#[derive(Debug, Deserialize)]
pub struct Settings {
pub app : App,
pub log : Log,
pub database : Database,
pub webserver : Webserver,
pub dev : Dev,
}
config_map!(r#"
Ajustes globales y valores predeterminados para las secciones *\[app\]*,
*\[log\]* y *\[webserver\]* de PageTop.
"#,
SETTINGS, Settings,
// [app]
"app.name" => "PageTop Application",
"app.description" => "Developed with the amazing PageTop framework.",
"app.theme" => "Bootsier",
"app.language" => "en-US",
"app.direction" => "ltr",
"app.startup_banner" => "Slant",
// [log]
"log.tracing" => "Info",
"log.rolling" => "Stdout",
"log.path" => "log",
"log.prefix" => "tracing.log",
"log.format" => "Full",
// [database]
"database.db_type" => "",
"database.db_name" => "",
"database.db_user" => "",
"database.db_pass" => "",
"database.db_host" => "localhost",
"database.db_port" => 0,
"database.max_pool_size" => 5,
// [webserver]
"webserver.bind_address" => "localhost",
"webserver.bind_port" => 8088,
// [dev]
"dev.static_files" => ""
); );
pub fn add_defaults(defaults: HashMap<&'static str, &'static str>) {
DEFAULTS.write().unwrap().extend(defaults);
}
pub fn get(key: &str) -> String {
match CONFIG.get_string(key) {
Ok(value) => value,
_ => match DEFAULTS.read().unwrap().get(key) {
Some(value) => String::from(*value),
_ => Default::default(),
},
}
}
pub fn get_value<T: FromStr + Default>(key: &str) -> T where <T as FromStr>::Err: Debug {
get(key).parse::<T>().unwrap_or(Default::default())
}

View file

@ -1,7 +1,7 @@
use crate::app; use crate::app;
use crate::base::component::{Container, Html}; use crate::base::component::{Container, Html};
use crate::concat_string; use crate::concat_string;
use crate::config::SETTINGS; use crate::config;
use crate::core::component::ComponentTrait; use crate::core::component::ComponentTrait;
use crate::html::{html, Favicon, Markup}; use crate::html::{html, Favicon, Markup};
use crate::response::page::{Page, PageContext, PageOp}; use crate::response::page::{Page, PageContext, PageOp};
@ -43,9 +43,9 @@ pub trait ThemeTrait: BaseTheme + Send + Sync {
@match page.title().get() { @match page.title().get() {
Some(t) => title { Some(t) => title {
(concat_string!(SETTINGS.app.name, " | ", t)) (concat_string!(config::get("app.name"), " | ", t))
}, },
None => title { (SETTINGS.app.name) } None => title { (config::get("app.name")) }
} }
@match page.description().get() { @match page.description().get() {

View file

@ -1,7 +1,45 @@
// EXTERNAL RE-EXPORTS. //! <div align="center">
//!
//! <img src="https://raw.githubusercontent.com/manuelcillero/pagetop/main/pagetop/static/pagetop-banner.png" />
//!
//! <h1>PageTop</h1>
//!
//! [![crate](https://img.shields.io/crates/v/pagetop.svg)](https://crates.io/crates/pagetop)
//! [![docs](https://docs.rs/pagetop/badge.svg)](https://docs.rs/pagetop)
//!
//! </div>
//!
//! **PageTop** es un entorno de desarrollo basado en Rust que reúne algunos de los crates más
//! estables y populares para crear soluciones web modulares, extensibles y configurables.
//!
//! PageTop añade una capa de abstracción para definir una interfaz única que ofrezca de partida:
//!
//! * Gestión de la configuración ([`config`]).
//!
//! * Registro de trazas y eventos de la aplicación ([`trace`]).
//!
//! * Localización ([`locale`]).
//!
//! * HTML en código ([`html`]).
//!
//! * Acceso a base de datos ([`db`]).
//!
//! * APIs esenciales para crear componentes, acciones, módulos y temas ([`core`]).
//!
//! * Tipos de respuestas a peticiones web ([`response`])
//!
//! * Base de componentes, módulos y temas ([`base`]).
//!
//! * Utilidades de carácter global ([`util`]).
//!
//! # 🚧 Advertencia
//!
//! **PageTop** sólo libera actualmente versiones de desarrollo. La API no es estable y los cambios
//! son constantes. No puede considerarse preparado hasta que se libere la versión **0.1.0**.
// GLOBAL.
pub use concat_string::concat_string; pub use concat_string::concat_string;
pub use doc_comment::doc_comment;
pub use once_cell::sync::Lazy as LazyStatic; pub use once_cell::sync::Lazy as LazyStatic;
// LOCAL. // LOCAL.
@ -9,7 +47,7 @@ pub use once_cell::sync::Lazy as LazyStatic;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use futures::executor::block_on as run_now; pub(crate) use futures::executor::block_on as run_now;
// PUBLIC APIs. // APIs PÚBLICAS.
// Gestión de la configuración. // Gestión de la configuración.
pub mod config; pub mod config;
@ -24,19 +62,19 @@ pub mod html;
#[cfg(feature = "database")] #[cfg(feature = "database")]
pub mod db; pub mod db;
// Prepare and run the application. // Prepara y ejecuta la aplicación.
pub mod app; pub mod app;
// Main APIs for components, hooks, modules and themes. // APIs esenciales para crear componentes, acciones, módulos y temas.
pub mod core; pub mod core;
// Tipos de respuestas web. // Tipos de respuestas a peticiones web.
pub mod response; pub mod response;
// Base de componentes, módulos y temas. // Base de componentes, módulos y temas.
pub mod base; pub mod base;
// Macros y funciones útiles. // Macros y funciones útiles.
pub mod util; pub mod util;
// INTERNAL RE-EXPORTS. // RE-EXPORTA API ÚNICA.
pub mod prelude; pub mod prelude;

View file

@ -5,7 +5,7 @@ pub use crate::{
args, concat_string, configure_service_for_static_files, pub_const_handler, LazyStatic, args, concat_string, configure_service_for_static_files, pub_const_handler, LazyStatic,
}; };
pub use crate::config::SETTINGS; pub use crate::config;
pub use crate::trace; pub use crate::trace;

View file

@ -1,11 +1,11 @@
use super::PageOp; use super::PageOp;
use crate::config::SETTINGS; use crate::config;
use crate::core::theme::{all::theme_by_single_name, ThemeStaticRef}; use crate::core::theme::{all::theme_by_single_name, ThemeStaticRef};
use crate::html::{html, Assets, Favicon, IdentifierValue, JavaScript, Markup, ModeJS, StyleSheet}; use crate::html::{html, Assets, Favicon, IdentifierValue, JavaScript, Markup, ModeJS, StyleSheet};
use crate::{base, concat_string, util, LazyStatic}; use crate::{base, concat_string, util, LazyStatic};
static DEFAULT_THEME: LazyStatic<ThemeStaticRef> = static DEFAULT_THEME: LazyStatic<ThemeStaticRef> =
LazyStatic::new(|| match theme_by_single_name(&SETTINGS.app.theme) { LazyStatic::new(|| match theme_by_single_name(&config::get("app.theme")) {
Some(theme) => theme, Some(theme) => theme,
None => &base::theme::bootsier::Bootsier, None => &base::theme::bootsier::Bootsier,
}); });

View file

@ -1,6 +1,6 @@
use super::{BeforeRenderPageHook, PageContext, PageOp, ResultPage, HOOK_BEFORE_RENDER_PAGE}; use super::{BeforeRenderPageHook, PageContext, PageOp, ResultPage, HOOK_BEFORE_RENDER_PAGE};
use crate::app::fatal_error::FatalError; use crate::app::fatal_error::FatalError;
use crate::config::SETTINGS; use crate::config;
use crate::core::component::*; use crate::core::component::*;
use crate::core::hook::{action_ref, run_actions}; use crate::core::hook::{action_ref, run_actions};
use crate::html::{html, AttributeValue, Classes, ClassesOp, Markup, DOCTYPE}; use crate::html::{html, AttributeValue, Classes, ClassesOp, Markup, DOCTYPE};
@ -9,7 +9,7 @@ use crate::{trace, LazyStatic};
use std::collections::HashMap; use std::collections::HashMap;
static DEFAULT_LANGUAGE: LazyStatic<Option<String>> = LazyStatic::new(|| { static DEFAULT_LANGUAGE: LazyStatic<Option<String>> = LazyStatic::new(|| {
let language = SETTINGS.app.language[..2].to_lowercase(); let language = config::get("app.language")[..2].to_lowercase();
if !language.is_empty() { if !language.is_empty() {
Some(language) Some(language)
} else { } else {
@ -18,7 +18,7 @@ static DEFAULT_LANGUAGE: LazyStatic<Option<String>> = LazyStatic::new(|| {
}); });
static DEFAULT_DIRECTION: LazyStatic<Option<String>> = LazyStatic::new(|| { static DEFAULT_DIRECTION: LazyStatic<Option<String>> = LazyStatic::new(|| {
let direction = SETTINGS.app.direction.to_lowercase(); let direction = config::get("app.direction").to_lowercase();
match direction.as_str() { match direction.as_str() {
"auto" => Some("auto".to_owned()), "auto" => Some("auto".to_owned()),
"ltr" => Some("ltr".to_owned()), "ltr" => Some("ltr".to_owned()),
@ -27,7 +27,7 @@ static DEFAULT_DIRECTION: LazyStatic<Option<String>> = LazyStatic::new(|| {
_ => { _ => {
trace::warn!( trace::warn!(
"Text direction \"{}\" not valid, {}", "Text direction \"{}\" not valid, {}",
SETTINGS.app.direction, config::get("app.direction"),
"check the settings file" "check the settings file"
); );
None None

View file

@ -169,7 +169,7 @@ macro_rules! args {
#[macro_export] #[macro_export]
macro_rules! configure_service_for_static_files { macro_rules! configure_service_for_static_files {
( $cfg:ident, $dir:expr, $embed:ident ) => {{ ( $cfg:ident, $dir:expr, $embed:ident ) => {{
let static_files = &$crate::config::SETTINGS.dev.static_files; let static_files = $crate::config::get("dev.static_files");
if static_files.is_empty() { if static_files.is_empty() {
$cfg.service($crate::app::ResourceFiles::new($dir, $embed())); $cfg.service($crate::app::ResourceFiles::new($dir, $embed()));
} else { } else {