🍻 Primera revista a las traducciones por contexto

This commit is contained in:
Manuel Cillero 2023-05-25 20:08:40 +02:00
parent 71b0b0889d
commit 0de26a4737
28 changed files with 307 additions and 187 deletions

View file

@ -14,11 +14,11 @@ impl ModuleTrait for Admin {
}
fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_ADMIN))
_t("module_name", Locale::From(&LOCALE_ADMIN))
}
fn description(&self) -> Option<String> {
Some(t("module_description", Locale::From(&LOCALE_ADMIN)))
Some(_t("module_description", Locale::From(&LOCALE_ADMIN)))
}
#[rustfmt::skip]

View file

@ -6,7 +6,7 @@ use pagetop_minimal::component::*;
pub async fn summary(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
let top_menu = MegaMenu::new()
.with_item(MegaMenuItem::label(
t("module_name", Locale::From(&LOCALE_ADMIN)).as_str(),
_t("module_name", Locale::From(&LOCALE_ADMIN)).as_str(),
))
.with_item(MegaMenuItem::link("Opción 2", "https://www.google.es"))
.with_item(MegaMenuItem::link_blank(

View file

@ -42,8 +42,13 @@ impl ThemeTrait for Bootsier {
pagetop_jquery::JQuery::add_jquery(page.context());
}
fn error_404_not_found(&self) -> HtmlMarkup {
HtmlMarkup::new().with(html! {
fn render_component(
&self,
component: &dyn ComponentTrait,
_rcx: &mut RenderContext,
) -> Option<Markup> {
match component.handle() {
ERROR_404 => Some(html! {
div class="jumbotron" {
div class="media" {
img
@ -53,19 +58,21 @@ impl ThemeTrait for Bootsier {
alt="Caution!";
div class="media-body" {
h1 class="display-4" { ("RESOURCE NOT FOUND") }
p class="lead" { (t("e404-description", Locale::From(&LOCALE_BOOTSIER))) }
p class="lead" { (_t("e404-description", Locale::From(&LOCALE_BOOTSIER))) }
hr class="my-4";
p { (t("e404-description", Locale::From(&LOCALE_BOOTSIER))) }
p { (_t("e404-description", Locale::From(&LOCALE_BOOTSIER))) }
a
class="btn btn-primary btn-lg"
href="/"
role="button"
{
(t("back-homepage", Locale::From(&LOCALE_BOOTSIER)))
(_t("back-homepage", Locale::From(&LOCALE_BOOTSIER)))
}
}
}
}
})
}),
_ => None,
}
}
}

View file

@ -15,11 +15,11 @@ impl ModuleTrait for HomeDemo {
}
fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_DEMOHOME))
_t("module_name", Locale::From(&LOCALE_DEMOHOME))
}
fn description(&self) -> Option<String> {
Some(t("module_description", Locale::From(&LOCALE_DEMOHOME)))
Some(_t("module_description", Locale::From(&LOCALE_DEMOHOME)))
}
fn dependencies(&self) -> Vec<ModuleStaticRef> {
@ -34,7 +34,7 @@ impl ModuleTrait for HomeDemo {
async fn demo(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
Page::new(request)
.with_title(t("page_title", Locale::From(&LOCALE_DEMOHOME)).as_str())
.with_title(_t("page_title", Locale::From(&LOCALE_DEMOHOME)).as_str())
.with_context(ContextOp::AddStyleSheet(StyleSheet::located(
"/homedemo/css/styles.css",
)))
@ -56,13 +56,13 @@ fn hello_world() -> Container {
.with_size(grid::ColumnSize::Is5of12)
.with_component(
Heading::h1(html! {
(t("page_title", Locale::From(&LOCALE_DEMOHOME)))
(_t("page_title", Locale::From(&LOCALE_DEMOHOME)))
})
.with_display(HeadingDisplay::Medium),
)
.with_component(
Paragraph::with(html! {
(e("hello_intro", Locale::With(&LOCALE_DEMOHOME, &args![
(_e("hello_intro", Locale::With(&LOCALE_DEMOHOME, &args![
"app" => format!(
"<span class=\"app-name\">{}</span>",
&config::SETTINGS.app.name,
@ -72,7 +72,7 @@ fn hello_world() -> Container {
.with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! {
(e("hello_powered", Locale::With(&LOCALE_DEMOHOME, &args![
(_e("hello_powered", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://pagetop.cillero.es",
@ -83,7 +83,7 @@ fn hello_world() -> Container {
.with_component(
Anchor::button(
"https://github.com/manuelcillero/pagetop",
html! { (t("hello_code", Locale::From(&LOCALE_DEMOHOME))) },
html! { (_t("hello_code", Locale::From(&LOCALE_DEMOHOME))) },
)
.with_target(AnchorTarget::Blank)
.with_left_icon(Icon::with("git"))
@ -92,7 +92,7 @@ fn hello_world() -> Container {
.with_component(
Anchor::link(
"#welcome",
html! { (t("hello_welcome", Locale::From(&LOCALE_DEMOHOME))) },
html! { (_t("hello_welcome", Locale::From(&LOCALE_DEMOHOME))) },
)
.with_left_icon(Icon::with("arrow-down-circle-fill"))
.with_classes(ClassesOp::Add, "welcome-link"),
@ -111,11 +111,11 @@ fn welcome() -> Container {
.with_id("welcome")
.with_classes(ClassesOp::Add, "welcome-col-text")
.with_component(Heading::h2(html! {
(t("welcome_page", Locale::From(&LOCALE_DEMOHOME)))
(_t("welcome_page", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(
Heading::h3(html! {
(e("welcome_subtitle", Locale::With(&LOCALE_DEMOHOME, &args![
(_e("welcome_subtitle", Locale::With(&LOCALE_DEMOHOME, &args![
"app" => format!(
"<span class=\"app-name\">{}</span>",
&config::SETTINGS.app.name
@ -126,12 +126,12 @@ fn welcome() -> Container {
)
.with_component(
Paragraph::with(html! {
(t("welcome_text1", Locale::From(&LOCALE_DEMOHOME)))
(_t("welcome_text1", Locale::From(&LOCALE_DEMOHOME)))
})
.with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(
html! { (t("welcome_text2", Locale::From(&LOCALE_DEMOHOME))) },
html! { (_t("welcome_text2", Locale::From(&LOCALE_DEMOHOME))) },
))
}
@ -148,23 +148,23 @@ fn about_pagetop() -> Container {
grid::Column::new()
.with_classes(ClassesOp::Add, "pagetop-col-text")
.with_component(Heading::h2(html! {
(t("pagetop_title", Locale::From(&LOCALE_DEMOHOME)))
(_t("pagetop_title", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(
Paragraph::with(html! {
(t("pagetop_text1", Locale::From(&LOCALE_DEMOHOME)))
(_t("pagetop_text1", Locale::From(&LOCALE_DEMOHOME)))
})
.with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! {
(t("pagetop_text2", Locale::From(&LOCALE_DEMOHOME)))
(_t("pagetop_text2", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(Paragraph::with(html! {
(e("pagetop_text3", Locale::With(&LOCALE_DEMOHOME, &args![
(_e("pagetop_text3", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop_website" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://docs.rs/pagetop/latest/pagetop",
t("pagetop_website", Locale::From(&LOCALE_DEMOHOME)),
_t("pagetop_website", Locale::From(&LOCALE_DEMOHOME)),
)
])))
})),
@ -179,11 +179,11 @@ fn promo_pagetop() -> Container {
grid::Column::new()
.with_classes(ClassesOp::Add, "promo-col-text")
.with_component(Heading::h2(html! {
(t("pagetop_promo_title", Locale::From(&LOCALE_DEMOHOME)))
(_t("pagetop_promo_title", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(
Paragraph::with(html! {
(e("pagetop_promo_text1", Locale::With(&LOCALE_DEMOHOME, &args![
(_e("pagetop_promo_text1", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://crates.io/crates/pagetop",
@ -216,16 +216,16 @@ fn reporting_issues() -> Container {
.with_classes(ClassesOp::Add, "reporting-col-text")
.with_size(grid::ColumnSize::Is6of12)
.with_component(Heading::h2(html! {
(t("report_problems_title", Locale::From(&LOCALE_DEMOHOME)))
(_t("report_problems_title", Locale::From(&LOCALE_DEMOHOME)))
}))
.with_component(
Paragraph::with(html! {
(t("report_problems_text1", Locale::From(&LOCALE_DEMOHOME)))
(_t("report_problems_text1", Locale::From(&LOCALE_DEMOHOME)))
})
.with_display(ParagraphDisplay::Small),
)
.with_component(Paragraph::with(html! {
(t("report_problems_text2", Locale::From(&LOCALE_DEMOHOME)))
(_t("report_problems_text2", Locale::From(&LOCALE_DEMOHOME)))
})),
),
)

View file

@ -22,7 +22,7 @@ pub enum AnchorTarget {
Context(String),
}
pub type AnchorIcon = ComponentsBundle;
pub type AnchorIcon = ComponentArc;
#[rustfmt::skip]
#[derive(Default)]
@ -74,7 +74,7 @@ impl ComponentTrait for Anchor {
target=[target]
{
(self.left_icon().render(rcx))
span { (*self.html()) }
(" ") span { (*self.html()) } (" ")
(self.right_icon().render(rcx))
}
}
@ -158,15 +158,13 @@ impl Anchor {
#[fn_builder]
pub fn alter_left_icon(&mut self, icon: Icon) -> &mut Self {
self.left_icon.clear();
self.left_icon.add(icon);
self.left_icon.replace(icon);
self
}
#[fn_builder]
pub fn alter_right_icon(&mut self, icon: Icon) -> &mut Self {
self.right_icon.clear();
self.right_icon.add(icon);
self.right_icon.replace(icon);
self
}

View file

@ -12,10 +12,10 @@ impl ModuleTrait for Menu {
}
fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_MENU))
_t("module_name", Locale::From(&LOCALE_MENU))
}
fn description(&self) -> Option<String> {
Some(t("module_description", Locale::From(&LOCALE_MENU)))
Some(_t("module_description", Locale::From(&LOCALE_MENU)))
}
}

View file

@ -15,11 +15,11 @@ impl ModuleTrait for Node {
}
fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_NODE))
_t("module_name", Locale::From(&LOCALE_NODE))
}
fn description(&self) -> Option<String> {
Some(t("module_description", Locale::From(&LOCALE_NODE)))
Some(_t("module_description", Locale::From(&LOCALE_NODE)))
}
fn configure_service(&self, cfg: &mut server::web::ServiceConfig) {

View file

@ -15,11 +15,11 @@ impl ModuleTrait for User {
}
fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_USER))
_t("module_name", Locale::From(&LOCALE_USER))
}
fn description(&self) -> Option<String> {
Some(t("module_description", Locale::From(&LOCALE_USER)))
Some(_t("module_description", Locale::From(&LOCALE_USER)))
}
fn dependencies(&self) -> Vec<ModuleStaticRef> {
@ -58,9 +58,9 @@ fn form_login() -> Form {
.with_element(
form_element::Input::textfield()
.with_name("name")
.with_label(t("username", Locale::From(&LOCALE_USER)).as_str())
.with_label(_t("username", Locale::From(&LOCALE_USER)).as_str())
.with_help_text(
t(
_t(
"username_help",
Locale::With(
&LOCALE_USER,
@ -74,10 +74,10 @@ fn form_login() -> Form {
.with_element(
form_element::Input::password()
.with_name("pass")
.with_label(t("password", Locale::From(&LOCALE_USER)).as_str())
.with_help_text(t("password_help", Locale::From(&LOCALE_USER)).as_str()),
.with_label(_t("password", Locale::From(&LOCALE_USER)).as_str())
.with_help_text(_t("password_help", Locale::From(&LOCALE_USER)).as_str()),
)
.with_element(form_element::Button::submit(
t("login", Locale::From(&LOCALE_USER)).as_str(),
_t("login", Locale::From(&LOCALE_USER)).as_str(),
))
}

View file

@ -54,7 +54,7 @@ tracing-unwrap = { version = "0.10.0", default-features = false }
tracing-actix-web = "0.7.4"
fluent-templates = "0.8.0"
unic-langid = "0.9.1"
unic-langid = { version = "0.9.1", features = ["macros"] }
actix-web = "4"
actix-session = { version = "0.7.2", features = ["cookie-session"] }

View file

@ -4,8 +4,8 @@ mod figfont;
use crate::core::{module, module::ModuleStaticRef};
use crate::html::Markup;
use crate::response::fatal_error::FatalError;
use crate::response::page::ResultPage;
use crate::response::FatalError;
use crate::{config, locale, server, trace, LazyStatic};
#[cfg(feature = "database")]

View file

@ -1,6 +1,12 @@
mod context;
pub use context::{ContextOp, RenderContext};
mod definition;
pub use definition::{component_mut, component_ref, AnyComponent, BaseComponent, ComponentTrait};
mod arc;
pub use arc::ComponentArc;
mod bundle;
pub use bundle::ComponentsBundle;

View file

@ -0,0 +1,32 @@
use crate::core::component::{ComponentTrait, RenderContext};
use crate::html::{html, Markup};
use std::sync::{Arc, RwLock};
#[derive(Clone, Default)]
pub struct ComponentArc(Option<Arc<RwLock<dyn ComponentTrait>>>);
impl ComponentArc {
pub fn new(component: impl ComponentTrait) -> Self {
ComponentArc(Some(Arc::new(RwLock::new(component))))
}
pub fn replace(&mut self, component: impl ComponentTrait) {
self.0 = Some(Arc::new(RwLock::new(component)));
}
pub fn weight(&self) -> isize {
match &self.0 {
Some(component) => component.read().unwrap().weight(),
_ => 0,
}
}
pub fn render(&self, rcx: &mut RenderContext) -> Markup {
html! {
@if let Some(component) = &self.0 {
(component.write().unwrap().render(rcx))
}
}
}
}

View file

@ -1,10 +1,8 @@
use crate::core::component::ComponentTrait;
use crate::html::{html, Markup, RenderContext};
use std::sync::{Arc, RwLock};
use crate::core::component::{ComponentArc, ComponentTrait, RenderContext};
use crate::html::{html, Markup};
#[derive(Clone, Default)]
pub struct ComponentsBundle(Vec<Arc<RwLock<dyn ComponentTrait>>>);
pub struct ComponentsBundle(Vec<ComponentArc>);
impl ComponentsBundle {
pub fn new() -> Self {
@ -12,13 +10,13 @@ impl ComponentsBundle {
}
pub fn new_with(component: impl ComponentTrait) -> Self {
let mut container = ComponentsBundle::new();
container.add(component);
container
let mut bundle = ComponentsBundle::new();
bundle.add(component);
bundle
}
pub fn add(&mut self, component: impl ComponentTrait) {
self.0.push(Arc::new(RwLock::new(component)));
self.0.push(ComponentArc::new(component));
}
pub fn clear(&mut self) {
@ -27,10 +25,10 @@ impl ComponentsBundle {
pub fn render(&self, rcx: &mut RenderContext) -> Markup {
let mut components = self.0.clone();
components.sort_by_key(|c| c.read().unwrap().weight());
components.sort_by_key(|c| c.weight());
html! {
@for c in components.iter() {
(" ")(c.write().unwrap().render(rcx))(" ")
(" ")(c.render(rcx))(" ")
}
}
}

View file

@ -1,5 +1,6 @@
use crate::core::module::{all::theme_by_single_name, ThemeStaticRef};
use crate::html::{html, Assets, IdentifierValue, JavaScript, Markup, StyleSheet};
use crate::locale::{LanguageIdentifier, LANGID};
use crate::server::HttpRequest;
use crate::{concat_string, config, util, LazyStatic};
@ -23,6 +24,7 @@ pub enum ContextOp {
#[rustfmt::skip]
pub struct RenderContext {
language : &'static LanguageIdentifier,
theme : ThemeStaticRef,
request : Option<HttpRequest>,
stylesheets: Assets<StyleSheet>,
@ -35,6 +37,7 @@ impl Default for RenderContext {
#[rustfmt::skip]
fn default() -> Self {
RenderContext {
language : &LANGID,
theme : *DEFAULT_THEME,
request : None,
stylesheets: Assets::<StyleSheet>::new(),
@ -81,6 +84,10 @@ impl RenderContext {
/// Context GETTERS.
pub(crate) fn language(&self) -> &LanguageIdentifier {
self.language
}
pub(crate) fn theme(&self) -> ThemeStaticRef {
self.theme
}

View file

@ -1,9 +1,12 @@
use crate::html::{html, Markup, RenderContext};
use crate::core::component::RenderContext;
use crate::html::{html, Markup};
use crate::util::single_type_name;
use crate::Handle;
use crate::{define_handle, Handle};
pub use std::any::Any as AnyComponent;
define_handle!(COMPONENT_UNDEFINED);
pub trait BaseComponent {
fn render(&mut self, rcx: &mut RenderContext) -> Markup;
}
@ -13,7 +16,9 @@ pub trait ComponentTrait: AnyComponent + BaseComponent + Send + Sync {
where
Self: Sized;
fn handle(&self) -> Handle;
fn handle(&self) -> Handle {
COMPONENT_UNDEFINED
}
fn name(&self) -> String {
single_type_name::<Self>().to_owned()

View file

@ -1,5 +1,5 @@
use crate::core::component::{AnyComponent, ComponentTrait};
use crate::html::{html, Markup, RenderContext};
use crate::core::component::{AnyComponent, ComponentTrait, RenderContext};
use crate::html::{html, Markup};
use crate::{define_handle, Handle};
define_handle!(HTML_MARKUP);

View file

@ -1,4 +1,4 @@
use crate::html::RenderContext;
use crate::core::component::RenderContext;
pub type IsRenderable = fn(&RenderContext) -> bool;

View file

@ -1,7 +1,7 @@
use super::ModuleTrait;
use crate::core::component::{ComponentTrait, HtmlMarkup};
use crate::html::{html, Favicon, Markup, RenderContext};
use crate::core::component::{ComponentTrait, RenderContext};
use crate::html::{html, Favicon, Markup};
use crate::response::page::Page;
use crate::{concat_string, config};
@ -118,20 +118,4 @@ pub trait ThemeTrait: ModuleTrait + Send + Sync {
}
*/
}
fn error_404_not_found(&self) -> HtmlMarkup {
HtmlMarkup::new().with(html! {
div {
h1 { ("RESOURCE NOT FOUND") }
}
})
}
fn error_403_access_denied(&self) -> HtmlMarkup {
HtmlMarkup::new().with(html! {
div {
h1 { ("FORBIDDEN ACCESS") }
}
})
}
}

View file

@ -8,9 +8,6 @@ pub use assets::javascript::{JavaScript, ModeJS};
pub use assets::stylesheet::{StyleSheet, TargetMedia};
pub use assets::Assets;
mod context;
pub use context::{ContextOp, RenderContext};
mod favicon;
pub use favicon::Favicon;

View file

@ -85,59 +85,12 @@ pub mod server;
// Tipos de respuestas a peticiones web.
pub mod response;
// Funciones útiles.
// Funciones útiles y macros declarativas.
pub mod util;
// Prepara y ejecuta la aplicación.
pub mod app;
// *************************************************************************************************
// MACROS DECLARATIVAS.
// *************************************************************************************************
#[macro_export]
/// Macro para construir grupos de pares clave-valor.
///
/// ```rust#ignore
/// let args = args![
/// "userName" => "Roberto",
/// "photoCount" => 3,
/// "userGender" => "male"
/// ];
/// ```
macro_rules! args {
( $($key:expr => $value:expr),* ) => {{
let mut a = std::collections::HashMap::new();
$(
a.insert(String::from($key), $value.into());
)*
a
}};
}
#[macro_export]
macro_rules! define_handle {
( $HANDLE:ident ) => {
pub const $HANDLE: $crate::Handle =
$crate::util::handle(module_path!(), file!(), line!(), column!());
};
}
#[macro_export]
macro_rules! serve_static_files {
( $cfg:ident, $dir:expr, $embed:ident ) => {{
let static_files = &$crate::config::SETTINGS.dev.static_files;
if static_files.is_empty() {
$cfg.service($crate::server::ResourceFiles::new($dir, $embed()));
} else {
$cfg.service(
$crate::server::ActixFiles::new($dir, $crate::concat_string!(static_files, $dir))
.show_files_listing(),
);
}
}};
}
// *************************************************************************************************
// RE-EXPORTA API ÚNICA.
// *************************************************************************************************

View file

@ -92,23 +92,37 @@
//! ```
use crate::html::{Markup, PreEscaped};
use crate::{config, trace, LazyStatic};
use crate::{args, config, trace, LazyStatic};
use unic_langid::LanguageIdentifier;
use unic_langid::langid;
pub use fluent_templates;
pub use fluent_templates::fluent_bundle::FluentValue;
pub use fluent_templates::{static_loader as static_locale, Loader, StaticLoader as Locales};
pub use unic_langid::LanguageIdentifier;
use std::collections::HashMap;
static LANGUAGES: LazyStatic<HashMap<String, (LanguageIdentifier, &str)>> = LazyStatic::new(|| {
args![
"en" => (langid!("en-US"), "English"),
"en-US" => (langid!("en-US"), "English (...)"),
"es" => (langid!("es-ES"), "Spanish"),
"es-ES" => (langid!("es-ES"), "Spanish (Spain)")
]
});
static DEFAULT_LANGID: LazyStatic<LanguageIdentifier> = LazyStatic::new(|| langid!("en-US"));
/// Almacena el Identificador de Idioma Unicode
/// ([Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier))
/// para la aplicación, obtenido de `SETTINGS.app.language`.
pub static LANGID: LazyStatic<LanguageIdentifier> =
LazyStatic::new(|| match config::SETTINGS.app.language.parse() {
Ok(language) => language,
Err(_) => {
pub static LANGID: LazyStatic<&LanguageIdentifier> =
LazyStatic::new(
|| match LANGUAGES.get(config::SETTINGS.app.language.as_str()) {
Some((langid, _)) => langid,
_ => {
trace::warn!(
"{}, {} \"{}\"! {}, {}",
"Failed to parse language",
@ -117,9 +131,10 @@ pub static LANGID: LazyStatic<LanguageIdentifier> =
"Using \"en-US\"",
"check the settings file",
);
"en-US".parse().unwrap()
&*DEFAULT_LANGID
}
});
},
);
#[macro_export]
/// Define un conjunto de elementos de localización y funciones locales de traducción.
@ -128,7 +143,7 @@ macro_rules! define_locale {
use $crate::locale::*;
static_locale! {
static $LOCALES = {
pub static $LOCALES = {
locales: $dir_locales,
$( core_locales: $core_locales, )?
fallback_language: "en-US",
@ -151,7 +166,7 @@ pub enum Locale<'a> {
),
}
pub fn t(key: &str, locale: Locale) -> String {
pub fn _t(key: &str, locale: Locale) -> String {
match locale {
Locale::From(locales) => locales.lookup(&LANGID, key).unwrap_or(key.to_string()),
Locale::With(locales, args) => locales
@ -164,6 +179,6 @@ pub fn t(key: &str, locale: Locale) -> String {
}
}
pub fn e(key: &str, locale: Locale) -> Markup {
PreEscaped(t(key, locale))
pub fn _e(key: &str, locale: Locale) -> Markup {
PreEscaped(_t(key, locale))
}

View file

@ -30,6 +30,7 @@ pub use crate::{hook_action, hook_before_render_component};
pub use crate::server;
pub use crate::server::HttpMessage;
pub use crate::response::{page::*, FatalError, ResponseError};
pub use crate::response::fatal_error::*;
pub use crate::response::{page::*, ResponseError};
pub use crate::app::Application;

View file

@ -4,5 +4,4 @@ pub use actix_web::ResponseError;
pub mod page;
mod fatal_error;
pub use fatal_error::FatalError;
pub mod fatal_error;

View file

@ -1,3 +1,8 @@
mod error403;
pub use error403::ERROR_403;
mod error404;
pub use error404::ERROR_404;
use crate::response::{page::Page, ResponseError};
use crate::server::http::{header::ContentType, StatusCode};
use crate::server::{HttpRequest, HttpResponse};
@ -24,11 +29,10 @@ impl fmt::Display for FatalError {
FatalError::BadRequest(_) => write!(f, "Bad Client Data"),
// Error 403.
FatalError::AccessDenied(request) => {
let mut error_page = Page::new(request.clone());
let error_content = error_page.context().theme().error_403_access_denied();
let error_page = Page::new(request.clone());
if let Ok(page) = error_page
.with_title("Error FORBIDDEN")
.with_this_in("region-content", error_content)
.with_this_in("region-content", error403::Error403)
.with_template("error")
.render()
{
@ -39,11 +43,10 @@ impl fmt::Display for FatalError {
}
// Error 404.
FatalError::NotFound(request) => {
let mut error_page = Page::new(request.clone());
let error_content = error_page.context().theme().error_404_not_found();
let error_page = Page::new(request.clone());
if let Ok(page) = error_page
.with_title("Error RESOURCE NOT FOUND")
.with_this_in("region-content", error_content)
.with_this_in("region-content", error404::Error404)
.with_template("error")
.render()
{

View file

@ -0,0 +1,33 @@
use crate::core::component::{AnyComponent, ComponentTrait, RenderContext};
use crate::html::{html, Markup};
use crate::{define_handle, Handle};
define_handle!(ERROR_403);
pub struct Error403;
impl ComponentTrait for Error403 {
fn new() -> Self {
Self
}
fn handle(&self) -> Handle {
ERROR_403
}
fn default_render(&self, _rcx: &mut RenderContext) -> Markup {
html! {
div {
h1 { ("FORBIDDEN ACCESS") }
}
}
}
fn as_ref_any(&self) -> &dyn AnyComponent {
self
}
fn as_mut_any(&mut self) -> &mut dyn AnyComponent {
self
}
}

View file

@ -0,0 +1,33 @@
use crate::core::component::{AnyComponent, ComponentTrait, RenderContext};
use crate::html::{html, Markup};
use crate::{define_handle, Handle};
define_handle!(ERROR_404);
pub struct Error404;
impl ComponentTrait for Error404 {
fn new() -> Self {
Self
}
fn handle(&self) -> Handle {
ERROR_404
}
fn default_render(&self, _rcx: &mut RenderContext) -> Markup {
html! {
div {
h1 { ("RESOURCE NOT FOUND") }
}
}
}
fn as_ref_any(&self) -> &dyn AnyComponent {
self
}
fn as_mut_any(&mut self) -> &mut dyn AnyComponent {
self
}
}

View file

@ -2,10 +2,8 @@ use super::{BeforeRenderPageHook, ResultPage, HOOK_BEFORE_RENDER_PAGE};
use crate::core::component::*;
use crate::core::hook::{action_ref, run_actions};
use crate::html::{
html, AttributeValue, Classes, ClassesOp, ContextOp, Favicon, Markup, RenderContext, DOCTYPE,
};
use crate::response::FatalError;
use crate::html::{html, AttributeValue, Classes, ClassesOp, Favicon, Markup, DOCTYPE};
use crate::response::fatal_error::FatalError;
use crate::{config, fn_builder, locale, server, trace, LazyStatic};
use std::collections::HashMap;

View file

@ -1,7 +1,11 @@
//! Funciones útiles.
//! Funciones útiles y macros declarativas.
use crate::Handle;
// *************************************************************************************************
// FUNCIONES ÚTILES.
// *************************************************************************************************
// https://stackoverflow.com/a/71464396
pub const fn handle(
module_path: &'static str,
@ -50,3 +54,50 @@ pub fn partial_type_name(type_name: &'static str, last: usize) -> &'static str {
pub fn single_type_name<T: ?Sized>() -> &'static str {
partial_type_name(std::any::type_name::<T>(), 1)
}
// *************************************************************************************************
// MACROS DECLARATIVAS.
// *************************************************************************************************
#[macro_export]
/// Macro para construir grupos de pares clave-valor.
///
/// ```rust#ignore
/// let args = args![
/// "userName" => "Roberto",
/// "photoCount" => 3,
/// "userGender" => "male"
/// ];
/// ```
macro_rules! args {
( $($key:expr => $value:expr),* ) => {{
let mut a = std::collections::HashMap::new();
$(
a.insert(String::from($key), $value.into());
)*
a
}};
}
#[macro_export]
macro_rules! define_handle {
( $HANDLE:ident ) => {
pub const $HANDLE: $crate::Handle =
$crate::util::handle(module_path!(), file!(), line!(), column!());
};
}
#[macro_export]
macro_rules! serve_static_files {
( $cfg:ident, $dir:expr, $embed:ident ) => {{
let static_files = &$crate::config::SETTINGS.dev.static_files;
if static_files.is_empty() {
$cfg.service($crate::server::ResourceFiles::new($dir, $embed()));
} else {
$cfg.service(
$crate::server::ActixFiles::new($dir, $crate::concat_string!(static_files, $dir))
.show_files_listing(),
);
}
}};
}