🧑‍💻 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(
"username_help",
&args![
"app" => SETTINGS.app.name.to_owned()
"app" => config::get("app.name").to_owned()
],
)
.as_str(),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -55,7 +55,7 @@ fn hello_world() -> Container {
.with_component(
Paragraph::with(html! {
(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),
@ -101,7 +101,7 @@ fn welcome() -> Container {
.with_component(
Heading::h3(html! {
(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),

View file

@ -1,130 +1,92 @@
//! 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 config_rs::{Config, File};
use serde::Deserialize;
use std::collections::HashMap;
use std::default::Default;
use std::env;
use std::fmt::Debug;
use std::str::FromStr;
use std::sync::RwLock;
/// Nombre del directorio donde se encuentra la configuración.
const CONFIG_DIR: &str = "config";
#[macro_export]
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
/// los archivos de configuración. Con [`config_map`] se asignarán los ajustes
/// 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.
pub static CONFIG: LazyStatic<Config> = LazyStatic::new(|| {
// Establece el modo de ejecución según el valor de la variable de entorno
// PAGETOP_RUN_MODE. Asume "default" por defecto.
/// Directorio donde se encuentran los archivos de configuración.
pub const CONFIG_DIR: &str = "config";
/// Carga los valores originales "clave = valor" de los archivos de configuración. Con
/// [`config_map`] se asignarán los ajustes 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.
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());
// 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.
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();
// Primero añade configuración común a todos los entornos. Opcional.
.add_source(File::with_name(&format!("{}/{}.toml", CONFIG_DIR, "common")).required(false))
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]
/// Asigna los ajustes específicos de la aplicación, o de un tema, módulo o
/// componente, en una estructura similar a [`SETTINGS`] con tipos de variables
/// seguros. Produce un *panic!* en caso de asignaciones no válidas.
macro_rules! config_map {
(
$doc:expr,
$SETTINGS:ident,
$Type:tt
$(, $key:expr => $value:expr)*
) => {
$crate::doc_comment! {
concat!($doc),
pub static $SETTINGS: $crate::LazyStatic<$Type> = $crate::LazyStatic::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),
}
});
}
};
}
#[rustfmt::skip]
#[derive(Debug, Deserialize)]
pub struct App {
pub name : String,
pub description : String,
pub theme : String,
pub language : String,
pub direction : String,
pub startup_banner: String,
pub run_mode : String,
}
#[rustfmt::skip]
#[derive(Debug, Deserialize)]
pub struct Log {
pub tracing : String,
pub rolling : String,
pub path : String,
pub prefix : String,
pub format : String,
}
#[rustfmt::skip]
#[derive(Debug, Deserialize)]
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,
static DEFAULTS: LazyStatic<RwLock<HashMap<&str, &str>>> = LazyStatic::new(||
RwLock::new(default_settings![
// [app]
"app.name" => "PageTop Application",
"app.description" => "Developed with the amazing PageTop framework.",
@ -146,13 +108,32 @@ Ajustes globales y valores predeterminados para las secciones *\[app\]*,
"database.db_user" => "",
"database.db_pass" => "",
"database.db_host" => "localhost",
"database.db_port" => 0,
"database.max_pool_size" => 5,
"database.db_port" => "0",
"database.max_pool_size" => "5",
// [webserver]
"webserver.bind_address" => "localhost",
"webserver.bind_port" => 8088,
"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::base::component::{Container, Html};
use crate::concat_string;
use crate::config::SETTINGS;
use crate::config;
use crate::core::component::ComponentTrait;
use crate::html::{html, Favicon, Markup};
use crate::response::page::{Page, PageContext, PageOp};
@ -43,9 +43,9 @@ pub trait ThemeTrait: BaseTheme + Send + Sync {
@match page.title().get() {
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() {

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 doc_comment::doc_comment;
pub use once_cell::sync::Lazy as LazyStatic;
// LOCAL.
@ -9,7 +47,7 @@ pub use once_cell::sync::Lazy as LazyStatic;
#[allow(unused_imports)]
pub(crate) use futures::executor::block_on as run_now;
// PUBLIC APIs.
// APIs PÚBLICAS.
// Gestión de la configuración.
pub mod config;
@ -24,19 +62,19 @@ pub mod html;
#[cfg(feature = "database")]
pub mod db;
// Prepare and run the application.
// Prepara y ejecuta la aplicación.
pub mod app;
// Main APIs for components, hooks, modules and themes.
// APIs esenciales para crear componentes, acciones, módulos y temas.
pub mod core;
// Tipos de respuestas web.
// Tipos de respuestas a peticiones web.
pub mod response;
// Base de componentes, módulos y temas.
pub mod base;
// Macros y funciones útiles.
pub mod util;
// INTERNAL RE-EXPORTS.
// RE-EXPORTA API ÚNICA.
pub mod prelude;

View file

@ -5,7 +5,7 @@ pub use crate::{
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;

View file

@ -1,11 +1,11 @@
use super::PageOp;
use crate::config::SETTINGS;
use crate::config;
use crate::core::theme::{all::theme_by_single_name, ThemeStaticRef};
use crate::html::{html, Assets, Favicon, IdentifierValue, JavaScript, Markup, ModeJS, StyleSheet};
use crate::{base, concat_string, util, LazyStatic};
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,
None => &base::theme::bootsier::Bootsier,
});

View file

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

View file

@ -169,7 +169,7 @@ macro_rules! args {
#[macro_export]
macro_rules! configure_service_for_static_files {
( $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() {
$cfg.service($crate::app::ResourceFiles::new($dir, $embed()));
} else {