♻️ Major code restructuring
This commit is contained in:
parent
a96e203bb3
commit
fa66d628a0
221 changed files with 228 additions and 315 deletions
351
src/config.rs
Normal file
351
src/config.rs
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
//! Retrieve and apply settings values from configuration files.
|
||||
//!
|
||||
//! Carga la configuración de la aplicación en forma de pares `clave = valor` recogidos en archivos
|
||||
//! [TOML](https://toml.io).
|
||||
//!
|
||||
//! La metodología [The Twelve-Factor App](https://12factor.net/es/) define **la configuración de
|
||||
//! una aplicación como todo lo que puede variar entre despliegues**, diferenciando entre entornos
|
||||
//! de desarrollo, pre-producción, producción, etc.
|
||||
//!
|
||||
//! A veces las aplicaciones guardan configuraciones como constantes en el código, lo que implica
|
||||
//! una violación de esta metodología. PageTop recomienda una **estricta separación entre código y
|
||||
//! configuración**. La configuración variará en cada tipo de despliegue, y el código no.
|
||||
//!
|
||||
//!
|
||||
//! # Cómo cargar los ajustes de configuración
|
||||
//!
|
||||
//! Si tu aplicación requiere archivos de configuración debes crear un directorio *config* al mismo
|
||||
//! nivel del archivo *Cargo.toml* de tu proyecto (o del ejecutable binario de la aplicación).
|
||||
//!
|
||||
//! PageTop se encargará de cargar todos los ajustes de configuración de tu aplicación leyendo los
|
||||
//! siguientes archivos TOML en este orden (todos los archivos son opcionales):
|
||||
//!
|
||||
//! 1. **config/common.toml**, útil para los ajustes comunes a cualquier entorno. Estos valores
|
||||
//! podrán ser sobrescritos al fusionar los archivos de configuración restantes.
|
||||
//!
|
||||
//! 2. **config/{file}.toml**, donde *{file}* se define con la variable de entorno
|
||||
//! `PAGETOP_RUN_MODE`:
|
||||
//!
|
||||
//! * Si no está definida se asumirá *default* por defecto y PageTop intentará cargar el archivo
|
||||
//! *config/default.toml* si existe.
|
||||
//!
|
||||
//! * De esta manera podrás tener diferentes ajustes 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.
|
||||
//!
|
||||
//! * Normalmente estos archivos suelen ser idóneos para incluir contraseñas o configuración
|
||||
//! sensible asociada al entorno correspondiente. Estos archivos no deberían ser publicados en
|
||||
//! el repositorio Git por razones de seguridad.
|
||||
//!
|
||||
//! 3. **config/local.toml**, para añadir o sobrescribir ajustes de los archivos anteriores.
|
||||
//!
|
||||
//!
|
||||
//! # Cómo añadir ajustes de configuración
|
||||
//!
|
||||
//! Para proporcionar a tu **módulo** sus propios ajustes de configuración, añade
|
||||
//! [*serde*](https://docs.rs/serde) en las dependencias de tu archivo *Cargo.toml* habilitando la
|
||||
//! característica `derive`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! serde = { version = "1.0", features = ["derive"] }
|
||||
//! ```
|
||||
//!
|
||||
//! Y luego inicializa con la macro [`default_settings!`](crate::default_settings) tus ajustes,
|
||||
//! usando tipos seguros y asignando los valores predefinidos para la estructura asociada:
|
||||
//!
|
||||
//! ```
|
||||
//! use pagetop::prelude::*;
|
||||
//! use serde::Deserialize;
|
||||
//!
|
||||
//! #[derive(Debug, Deserialize)]
|
||||
//! pub struct Settings {
|
||||
//! pub myapp: MyApp,
|
||||
//! }
|
||||
//!
|
||||
//! #[derive(Debug, Deserialize)]
|
||||
//! pub struct MyApp {
|
||||
//! pub name: String,
|
||||
//! pub description: Option<String>,
|
||||
//! pub width: u16,
|
||||
//! pub height: u16,
|
||||
//! }
|
||||
//!
|
||||
//! default_settings!(
|
||||
//! // [myapp]
|
||||
//! "myapp.name" => "Value Name",
|
||||
//! "myapp.width" => 900,
|
||||
//! "myapp.height" => 320,
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! De hecho, así se declaran los ajustes globales de la configuración (ver [`SETTINGS`]).
|
||||
//!
|
||||
//! Puedes usar la [sintaxis TOML](https://toml.io/en/v1.0.0#table) para añadir tu nueva sección
|
||||
//! `[myapp]` en los archivos de configuración, del mismo modo que se añaden `[log]` o `[server]` en
|
||||
//! los ajustes globales (ver [`Settings`]).
|
||||
//!
|
||||
//! Se recomienda inicializar todos los ajustes con valores predefinidos, o utilizar la notación
|
||||
//! `Option<T>` si van a ser tratados en el código como opcionales.
|
||||
//!
|
||||
//! Si no pueden inicializarse correctamente los ajustes de configuración, entonces la aplicación
|
||||
//! ejecutará un panic! y detendrá la ejecución.
|
||||
//!
|
||||
//! Los ajustes de configuración siempre serán de sólo lectura.
|
||||
//!
|
||||
//!
|
||||
//! # Cómo usar tus nuevos ajustes de configuración
|
||||
//!
|
||||
//! ```
|
||||
//! use pagetop::prelude::*;
|
||||
//!
|
||||
//! fn global_settings() {
|
||||
//! println!("App name: {}", &config::SETTINGS.app.name);
|
||||
//! println!("App description: {}", &config::SETTINGS.app.description);
|
||||
//! println!("Value of PAGETOP_RUN_MODE: {}", &config::SETTINGS.app.run_mode);
|
||||
//! }
|
||||
//!
|
||||
//! fn package_settings() {
|
||||
//! println!("{} - {:?}", &SETTINGS.myapp.name, &SETTINGS.myapp.description);
|
||||
//! println!("{}", &SETTINGS.myapp.width);
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
mod data;
|
||||
mod de;
|
||||
mod error;
|
||||
mod file;
|
||||
mod path;
|
||||
mod source;
|
||||
mod value;
|
||||
|
||||
use crate::config::data::ConfigData;
|
||||
use crate::config::file::File;
|
||||
use crate::{concat_string, LazyStatic};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use std::env;
|
||||
|
||||
/// Directorio donde se encuentran los archivos de configuración.
|
||||
const CONFIG_DIR: &str = "config";
|
||||
|
||||
/// Valores originales de la configuración en forma de pares `clave = valor` recogidos de los
|
||||
/// archivos de configuración.
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub static CONFIG: LazyStatic<ConfigData> = 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());
|
||||
|
||||
// Inicializa los ajustes.
|
||||
let mut settings = ConfigData::default();
|
||||
|
||||
// Combina los archivos (opcionales) de configuración y asigna el modo de ejecución.
|
||||
settings
|
||||
// Primero añade la configuración común a todos los entornos. Por defecto 'common.toml'.
|
||||
.merge(
|
||||
File::with_name(&concat_string!(CONFIG_DIR, "/common.toml"))
|
||||
.required(false)
|
||||
).unwrap()
|
||||
// Añade la configuración específica del entorno. Por defecto 'default.toml'.
|
||||
.merge(
|
||||
File::with_name(&concat_string!(CONFIG_DIR, "/", run_mode, ".toml"))
|
||||
.required(false)
|
||||
).unwrap()
|
||||
// Añade la configuración local reservada del entorno. Por defecto 'default.local.toml'.
|
||||
.merge(
|
||||
File::with_name(&concat_string!(CONFIG_DIR, "/local.", run_mode, ".toml"))
|
||||
.required(false),
|
||||
).unwrap()
|
||||
// Añade la configuración local reservada general. Por defecto 'local.toml'.
|
||||
.merge(
|
||||
File::with_name(&concat_string!(CONFIG_DIR, "/local.toml"))
|
||||
.required(false)
|
||||
).unwrap()
|
||||
// Salvaguarda el modo de ejecución.
|
||||
.set("app.run_mode", run_mode)
|
||||
.unwrap();
|
||||
|
||||
settings
|
||||
});
|
||||
|
||||
#[macro_export]
|
||||
/// Define un conjunto de ajustes de configuración usando tipos seguros y valores predefinidos.
|
||||
///
|
||||
/// Detiene la aplicación con un panic! si no pueden asignarse los ajustes de configuración.
|
||||
///
|
||||
/// Ver [`Cómo añadir ajustes de configuración`](config/index.html#cómo-añadir-ajustes-de-configuración).
|
||||
macro_rules! default_settings {
|
||||
( $($key:literal => $value:literal),* $(,)? ) => {
|
||||
#[doc = concat!(
|
||||
"Assigned or predefined values for configuration settings associated to the ",
|
||||
"[`Settings`] type."
|
||||
)]
|
||||
pub static SETTINGS: $crate::LazyStatic<Settings> = $crate::LazyStatic::new(|| {
|
||||
let mut settings = $crate::config::CONFIG.clone();
|
||||
$(
|
||||
settings.set_default($key, $value).unwrap();
|
||||
)*
|
||||
match settings.try_into() {
|
||||
Ok(s) => s,
|
||||
Err(e) => panic!("Error parsing settings: {}", e),
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
/// Configuration settings for the [`[app]`](App), [`[database]`](Database), [`[dev]`](Dev),
|
||||
/// [`[log]`](Log), and [`[server]`](Server) sections (see [`SETTINGS`]).
|
||||
pub struct Settings {
|
||||
pub app: App,
|
||||
pub database: Database,
|
||||
pub dev: Dev,
|
||||
pub log: Log,
|
||||
pub server: Server,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
/// Section `[app]` of the configuration settings.
|
||||
///
|
||||
/// See [`Settings`].
|
||||
pub struct App {
|
||||
/// El nombre de la aplicación.
|
||||
/// Por defecto: *"PageTop App"*.
|
||||
pub name: String,
|
||||
/// Una descripción breve de la aplicación.
|
||||
/// Por defecto: *"Developed with the awesome PageTop framework."*.
|
||||
pub description: String,
|
||||
/// Tema predeterminado.
|
||||
/// Por defecto: *"Default"*.
|
||||
pub theme: String,
|
||||
/// Idioma (localización) predeterminado.
|
||||
/// Por defecto: *"en-US"*.
|
||||
pub language: String,
|
||||
/// Dirección predeterminada para el texto: *"ltr"* (de izquierda a derecha), *"rtl"* (de
|
||||
/// derecha a izquierda) o *"auto"*.
|
||||
/// Por defecto: *"ltr"*.
|
||||
pub direction: String,
|
||||
/// Rótulo de texto ASCII al arrancar: *"Off"*, *"Slant"*, *"Small"*, *"Speed"* o *"Starwars"*.
|
||||
/// Por defecto: *"Slant"*.
|
||||
pub startup_banner: String,
|
||||
/// Por defecto: según variable de entorno `PAGETOP_RUN_MODE`, o *"default"* si no lo está.
|
||||
pub run_mode: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
/// Section `[database]` of the configuration settings.
|
||||
///
|
||||
/// See [`Settings`].
|
||||
pub struct Database {
|
||||
/// Tipo de base de datos: *"mysql"*, *"postgres"* ó *"sqlite"*.
|
||||
/// Por defecto: *""*.
|
||||
pub db_type: String,
|
||||
/// Nombre (para mysql/postgres) o referencia (para sqlite) de la base de datos.
|
||||
/// Por defecto: *""*.
|
||||
pub db_name: String,
|
||||
/// Usuario de conexión a la base de datos (para mysql/postgres).
|
||||
/// Por defecto: *""*.
|
||||
pub db_user: String,
|
||||
/// Contraseña para la conexión a la base de datos (para mysql/postgres).
|
||||
/// Por defecto: *""*.
|
||||
pub db_pass: String,
|
||||
/// Servidor de conexión a la base de datos (para mysql/postgres).
|
||||
/// Por defecto: *"localhost"*.
|
||||
pub db_host: String,
|
||||
/// Puerto de conexión a la base de datos, normalmente 3306 (para mysql) ó 5432 (para postgres).
|
||||
/// Por defecto: *0*.
|
||||
pub db_port: u16,
|
||||
/// Número máximo de conexiones habilitadas.
|
||||
/// Por defecto: *5*.
|
||||
pub max_pool_size: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
/// Section `[dev]` of the configuration settings.
|
||||
///
|
||||
/// See [`Settings`].
|
||||
pub struct Dev {
|
||||
/// Los archivos estáticos requeridos por la aplicación se integran de manera predeterminada en
|
||||
/// el binario ejecutable. Sin embargo, durante el desarrollo puede resultar útil servir estos
|
||||
/// archivos desde su propio directorio para evitar recompilar cada vez que se modifican. En
|
||||
/// este caso bastaría con indicar la ruta completa al directorio raíz del proyecto.
|
||||
/// Por defecto: *""*.
|
||||
pub pagetop_project_dir: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
/// Section `[log]` of the configuration settings.
|
||||
///
|
||||
/// See [`Settings`].
|
||||
pub struct Log {
|
||||
/// Filtro, o combinación de filtros separados por coma, para la traza de ejecución: *"Error"*,
|
||||
/// *"Warn"*, *"Info"*, *"Debug"* o *"Trace"*.
|
||||
/// Por ejemplo: "Error,actix_server::builder=Info,tracing_actix_web=Debug".
|
||||
/// Por defecto: *"Info"*.
|
||||
pub tracing: String,
|
||||
/// Muestra la traza en el terminal (*"Stdout"*) o queda registrada en archivos con rotación
|
||||
/// *"Daily"*, *"Hourly"*, *"Minutely"* o *"Endless"*.
|
||||
/// Por defecto: *"Stdout"*.
|
||||
pub rolling: String,
|
||||
/// Directorio para los archivos de traza (si `rolling` != *"Stdout"*).
|
||||
/// Por defecto: *"log"*.
|
||||
pub path: String,
|
||||
/// Prefijo para los archivos de traza (si `rolling` != *"Stdout"*).
|
||||
/// Por defecto: *"tracing.log"*.
|
||||
pub prefix: String,
|
||||
/// Presentación de las trazas. Puede ser *"Full"*, *"Compact"*, *"Pretty"* o *"Json"*.
|
||||
/// Por defecto: *"Full"*.
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
/// Section `[server]` of the configuration settings.
|
||||
///
|
||||
/// See [`Settings`].
|
||||
pub struct Server {
|
||||
/// Dirección del servidor web.
|
||||
/// Por defecto: *"localhost"*.
|
||||
pub bind_address: String,
|
||||
/// Puerto del servidor web.
|
||||
/// Por defecto: *8088*.
|
||||
pub bind_port: u16,
|
||||
/// Duración en segundos para la sesión (0 indica "hasta que se cierre el navegador").
|
||||
/// Por defecto: *604800* (7 días).
|
||||
pub session_lifetime: i64,
|
||||
}
|
||||
|
||||
default_settings!(
|
||||
// [app]
|
||||
"app.name" => "PageTop App",
|
||||
"app.description" => "Developed with the awesome PageTop framework.",
|
||||
"app.theme" => "Default",
|
||||
"app.language" => "en-US",
|
||||
"app.direction" => "ltr",
|
||||
"app.startup_banner" => "Slant",
|
||||
|
||||
// [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,
|
||||
|
||||
// [dev]
|
||||
"dev.pagetop_project_dir" => "",
|
||||
|
||||
// [log]
|
||||
"log.tracing" => "Info",
|
||||
"log.rolling" => "Stdout",
|
||||
"log.path" => "log",
|
||||
"log.prefix" => "tracing.log",
|
||||
"log.format" => "Full",
|
||||
|
||||
// [server]
|
||||
"server.bind_address" => "localhost",
|
||||
"server.bind_port" => 8088,
|
||||
"server.session_lifetime" => 604800,
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue