From 0de26a4737e18ef23b11e7f5314533661029d15f Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Thu, 25 May 2023 20:08:40 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=BB=20Primera=20revista=20a=20las=20tr?= =?UTF-8?q?aducciones=20por=20contexto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pagetop-admin/src/lib.rs | 4 +- pagetop-admin/src/summary.rs | 2 +- pagetop-bootsier/src/lib.rs | 51 ++++++++++------- pagetop-homedemo/src/lib.rs | 44 +++++++------- pagetop-minimal/src/component/anchor.rs | 10 ++-- pagetop-minimal/src/module/menu.rs | 4 +- pagetop-node/src/lib.rs | 4 +- pagetop-user/src/lib.rs | 14 ++--- pagetop/Cargo.toml | 2 +- pagetop/src/app.rs | 2 +- pagetop/src/core/component.rs | 6 ++ pagetop/src/core/component/arc.rs | 32 +++++++++++ pagetop/src/core/component/bundle.rs | 20 +++---- .../src/{html => core/component}/context.rs | 7 +++ pagetop/src/core/component/definition.rs | 11 +++- pagetop/src/core/component/html_markup.rs | 4 +- pagetop/src/core/component/renderable.rs | 2 +- pagetop/src/core/module/theme.rs | 20 +------ pagetop/src/html.rs | 3 - pagetop/src/lib.rs | 49 +--------------- pagetop/src/locale.rs | 57 ++++++++++++------- pagetop/src/prelude.rs | 3 +- pagetop/src/response.rs | 3 +- pagetop/src/response/fatal_error.rs | 15 +++-- pagetop/src/response/fatal_error/error403.rs | 33 +++++++++++ pagetop/src/response/fatal_error/error404.rs | 33 +++++++++++ pagetop/src/response/page/definition.rs | 6 +- pagetop/src/util.rs | 53 ++++++++++++++++- 28 files changed, 307 insertions(+), 187 deletions(-) create mode 100644 pagetop/src/core/component/arc.rs rename pagetop/src/{html => core/component}/context.rs (94%) create mode 100644 pagetop/src/response/fatal_error/error403.rs create mode 100644 pagetop/src/response/fatal_error/error404.rs diff --git a/pagetop-admin/src/lib.rs b/pagetop-admin/src/lib.rs index a6b211ff..a2d30ead 100644 --- a/pagetop-admin/src/lib.rs +++ b/pagetop-admin/src/lib.rs @@ -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 { - Some(t("module_description", Locale::From(&LOCALE_ADMIN))) + Some(_t("module_description", Locale::From(&LOCALE_ADMIN))) } #[rustfmt::skip] diff --git a/pagetop-admin/src/summary.rs b/pagetop-admin/src/summary.rs index 5abf2e75..de844279 100644 --- a/pagetop-admin/src/summary.rs +++ b/pagetop-admin/src/summary.rs @@ -6,7 +6,7 @@ use pagetop_minimal::component::*; pub async fn summary(request: server::HttpRequest) -> ResultPage { 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( diff --git a/pagetop-bootsier/src/lib.rs b/pagetop-bootsier/src/lib.rs index 8fa08dd1..f7905663 100644 --- a/pagetop-bootsier/src/lib.rs +++ b/pagetop-bootsier/src/lib.rs @@ -42,30 +42,37 @@ impl ThemeTrait for Bootsier { pagetop_jquery::JQuery::add_jquery(page.context()); } - fn error_404_not_found(&self) -> HtmlMarkup { - HtmlMarkup::new().with(html! { - div class="jumbotron" { - div class="media" { - img - src="/bootsier/images/caution.png" - class="mr-4" - style="width: 20%; max-width: 188px" - alt="Caution!"; - div class="media-body" { - h1 class="display-4" { ("RESOURCE NOT FOUND") } - p class="lead" { (t("e404-description", Locale::From(&LOCALE_BOOTSIER))) } - hr class="my-4"; - 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))) + fn render_component( + &self, + component: &dyn ComponentTrait, + _rcx: &mut RenderContext, + ) -> Option { + match component.handle() { + ERROR_404 => Some(html! { + div class="jumbotron" { + div class="media" { + img + src="/bootsier/images/caution.png" + class="mr-4" + style="width: 20%; max-width: 188px" + alt="Caution!"; + div class="media-body" { + h1 class="display-4" { ("RESOURCE NOT FOUND") } + p class="lead" { (_t("e404-description", Locale::From(&LOCALE_BOOTSIER))) } + hr class="my-4"; + 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))) + } } } } - } - }) + }), + _ => None, + } } } diff --git a/pagetop-homedemo/src/lib.rs b/pagetop-homedemo/src/lib.rs index 15d1c3b1..38a1a171 100644 --- a/pagetop-homedemo/src/lib.rs +++ b/pagetop-homedemo/src/lib.rs @@ -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 { - Some(t("module_description", Locale::From(&LOCALE_DEMOHOME))) + Some(_t("module_description", Locale::From(&LOCALE_DEMOHOME))) } fn dependencies(&self) -> Vec { @@ -34,7 +34,7 @@ impl ModuleTrait for HomeDemo { async fn demo(request: server::HttpRequest) -> ResultPage { 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!( "{}", &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!( "{}", "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!( "{}", &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!( "{}", "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!( "{}", "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))) })), ), ) diff --git a/pagetop-minimal/src/component/anchor.rs b/pagetop-minimal/src/component/anchor.rs index 4e2381f9..2f9236e0 100644 --- a/pagetop-minimal/src/component/anchor.rs +++ b/pagetop-minimal/src/component/anchor.rs @@ -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 } diff --git a/pagetop-minimal/src/module/menu.rs b/pagetop-minimal/src/module/menu.rs index 177e811a..0e0c2d15 100644 --- a/pagetop-minimal/src/module/menu.rs +++ b/pagetop-minimal/src/module/menu.rs @@ -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 { - Some(t("module_description", Locale::From(&LOCALE_MENU))) + Some(_t("module_description", Locale::From(&LOCALE_MENU))) } } diff --git a/pagetop-node/src/lib.rs b/pagetop-node/src/lib.rs index 766e2fce..5d3efac3 100644 --- a/pagetop-node/src/lib.rs +++ b/pagetop-node/src/lib.rs @@ -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 { - 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) { diff --git a/pagetop-user/src/lib.rs b/pagetop-user/src/lib.rs index 9bc4a16b..9e3b9d8f 100644 --- a/pagetop-user/src/lib.rs +++ b/pagetop-user/src/lib.rs @@ -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 { - Some(t("module_description", Locale::From(&LOCALE_USER))) + Some(_t("module_description", Locale::From(&LOCALE_USER))) } fn dependencies(&self) -> Vec { @@ -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(), )) } diff --git a/pagetop/Cargo.toml b/pagetop/Cargo.toml index 9c1cdde5..88787324 100644 --- a/pagetop/Cargo.toml +++ b/pagetop/Cargo.toml @@ -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"] } diff --git a/pagetop/src/app.rs b/pagetop/src/app.rs index d69f5b8e..ca0fc6be 100644 --- a/pagetop/src/app.rs +++ b/pagetop/src/app.rs @@ -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")] diff --git a/pagetop/src/core/component.rs b/pagetop/src/core/component.rs index cd596397..c17485db 100644 --- a/pagetop/src/core/component.rs +++ b/pagetop/src/core/component.rs @@ -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; diff --git a/pagetop/src/core/component/arc.rs b/pagetop/src/core/component/arc.rs new file mode 100644 index 00000000..3341940a --- /dev/null +++ b/pagetop/src/core/component/arc.rs @@ -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>>); + +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)) + } + } + } +} diff --git a/pagetop/src/core/component/bundle.rs b/pagetop/src/core/component/bundle.rs index 0a0e8c95..4f25052a 100644 --- a/pagetop/src/core/component/bundle.rs +++ b/pagetop/src/core/component/bundle.rs @@ -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>>); +pub struct ComponentsBundle(Vec); 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))(" ") } } } diff --git a/pagetop/src/html/context.rs b/pagetop/src/core/component/context.rs similarity index 94% rename from pagetop/src/html/context.rs rename to pagetop/src/core/component/context.rs index 42c05b5b..a68fe290 100644 --- a/pagetop/src/html/context.rs +++ b/pagetop/src/core/component/context.rs @@ -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, stylesheets: Assets, @@ -35,6 +37,7 @@ impl Default for RenderContext { #[rustfmt::skip] fn default() -> Self { RenderContext { + language : &LANGID, theme : *DEFAULT_THEME, request : None, stylesheets: Assets::::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 } diff --git a/pagetop/src/core/component/definition.rs b/pagetop/src/core/component/definition.rs index 85e7fac8..db683062 100644 --- a/pagetop/src/core/component/definition.rs +++ b/pagetop/src/core/component/definition.rs @@ -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::().to_owned() diff --git a/pagetop/src/core/component/html_markup.rs b/pagetop/src/core/component/html_markup.rs index a4d62d9b..3b513707 100644 --- a/pagetop/src/core/component/html_markup.rs +++ b/pagetop/src/core/component/html_markup.rs @@ -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); diff --git a/pagetop/src/core/component/renderable.rs b/pagetop/src/core/component/renderable.rs index aef5bb92..ce2f302c 100644 --- a/pagetop/src/core/component/renderable.rs +++ b/pagetop/src/core/component/renderable.rs @@ -1,4 +1,4 @@ -use crate::html::RenderContext; +use crate::core::component::RenderContext; pub type IsRenderable = fn(&RenderContext) -> bool; diff --git a/pagetop/src/core/module/theme.rs b/pagetop/src/core/module/theme.rs index 25fa0720..9cd81ae9 100644 --- a/pagetop/src/core/module/theme.rs +++ b/pagetop/src/core/module/theme.rs @@ -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") } - } - }) - } } diff --git a/pagetop/src/html.rs b/pagetop/src/html.rs index 6d07c9eb..fcfbbab7 100644 --- a/pagetop/src/html.rs +++ b/pagetop/src/html.rs @@ -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; diff --git a/pagetop/src/lib.rs b/pagetop/src/lib.rs index ff551e9e..1a509744 100644 --- a/pagetop/src/lib.rs +++ b/pagetop/src/lib.rs @@ -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. // ************************************************************************************************* diff --git a/pagetop/src/locale.rs b/pagetop/src/locale.rs index 09f0313d..13961e39 100644 --- a/pagetop/src/locale.rs +++ b/pagetop/src/locale.rs @@ -92,34 +92,49 @@ //! ``` 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> = 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 = 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 = - LazyStatic::new(|| match config::SETTINGS.app.language.parse() { - Ok(language) => language, - Err(_) => { - trace::warn!( - "{}, {} \"{}\"! {}, {}", - "Failed to parse language", - "unrecognized Unicode Language Identifier", - config::SETTINGS.app.language, - "Using \"en-US\"", - "check the settings file", - ); - "en-US".parse().unwrap() - } - }); +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", + "unrecognized Unicode Language Identifier", + config::SETTINGS.app.language, + "Using \"en-US\"", + "check the settings file", + ); + &*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)) } diff --git a/pagetop/src/prelude.rs b/pagetop/src/prelude.rs index 947ebe3a..2097aeec 100644 --- a/pagetop/src/prelude.rs +++ b/pagetop/src/prelude.rs @@ -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; diff --git a/pagetop/src/response.rs b/pagetop/src/response.rs index 569dcf50..985d918d 100644 --- a/pagetop/src/response.rs +++ b/pagetop/src/response.rs @@ -4,5 +4,4 @@ pub use actix_web::ResponseError; pub mod page; -mod fatal_error; -pub use fatal_error::FatalError; +pub mod fatal_error; diff --git a/pagetop/src/response/fatal_error.rs b/pagetop/src/response/fatal_error.rs index 0c17dd8e..9019818b 100644 --- a/pagetop/src/response/fatal_error.rs +++ b/pagetop/src/response/fatal_error.rs @@ -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() { diff --git a/pagetop/src/response/fatal_error/error403.rs b/pagetop/src/response/fatal_error/error403.rs new file mode 100644 index 00000000..80ab48e9 --- /dev/null +++ b/pagetop/src/response/fatal_error/error403.rs @@ -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 + } +} diff --git a/pagetop/src/response/fatal_error/error404.rs b/pagetop/src/response/fatal_error/error404.rs new file mode 100644 index 00000000..cc808400 --- /dev/null +++ b/pagetop/src/response/fatal_error/error404.rs @@ -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 + } +} diff --git a/pagetop/src/response/page/definition.rs b/pagetop/src/response/page/definition.rs index 25adbfb7..a3966374 100644 --- a/pagetop/src/response/page/definition.rs +++ b/pagetop/src/response/page/definition.rs @@ -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; diff --git a/pagetop/src/util.rs b/pagetop/src/util.rs index 986f43d4..9f58bc99 100644 --- a/pagetop/src/util.rs +++ b/pagetop/src/util.rs @@ -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() -> &'static str { partial_type_name(std::any::type_name::(), 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(), + ); + } + }}; +}