🍻 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 { fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_ADMIN)) _t("module_name", Locale::From(&LOCALE_ADMIN))
} }
fn description(&self) -> Option<String> { fn description(&self) -> Option<String> {
Some(t("module_description", Locale::From(&LOCALE_ADMIN))) Some(_t("module_description", Locale::From(&LOCALE_ADMIN)))
} }
#[rustfmt::skip] #[rustfmt::skip]

View file

@ -6,7 +6,7 @@ use pagetop_minimal::component::*;
pub async fn summary(request: server::HttpRequest) -> ResultPage<Markup, FatalError> { pub async fn summary(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
let top_menu = MegaMenu::new() let top_menu = MegaMenu::new()
.with_item(MegaMenuItem::label( .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("Opción 2", "https://www.google.es"))
.with_item(MegaMenuItem::link_blank( .with_item(MegaMenuItem::link_blank(

View file

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

View file

@ -15,11 +15,11 @@ impl ModuleTrait for HomeDemo {
} }
fn name(&self) -> String { fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_DEMOHOME)) _t("module_name", Locale::From(&LOCALE_DEMOHOME))
} }
fn description(&self) -> Option<String> { 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> { fn dependencies(&self) -> Vec<ModuleStaticRef> {
@ -34,7 +34,7 @@ impl ModuleTrait for HomeDemo {
async fn demo(request: server::HttpRequest) -> ResultPage<Markup, FatalError> { async fn demo(request: server::HttpRequest) -> ResultPage<Markup, FatalError> {
Page::new(request) 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( .with_context(ContextOp::AddStyleSheet(StyleSheet::located(
"/homedemo/css/styles.css", "/homedemo/css/styles.css",
))) )))
@ -56,13 +56,13 @@ fn hello_world() -> Container {
.with_size(grid::ColumnSize::Is5of12) .with_size(grid::ColumnSize::Is5of12)
.with_component( .with_component(
Heading::h1(html! { Heading::h1(html! {
(t("page_title", Locale::From(&LOCALE_DEMOHOME))) (_t("page_title", Locale::From(&LOCALE_DEMOHOME)))
}) })
.with_display(HeadingDisplay::Medium), .with_display(HeadingDisplay::Medium),
) )
.with_component( .with_component(
Paragraph::with(html! { Paragraph::with(html! {
(e("hello_intro", Locale::With(&LOCALE_DEMOHOME, &args![ (_e("hello_intro", Locale::With(&LOCALE_DEMOHOME, &args![
"app" => format!( "app" => format!(
"<span class=\"app-name\">{}</span>", "<span class=\"app-name\">{}</span>",
&config::SETTINGS.app.name, &config::SETTINGS.app.name,
@ -72,7 +72,7 @@ fn hello_world() -> Container {
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with(html! { .with_component(Paragraph::with(html! {
(e("hello_powered", Locale::With(&LOCALE_DEMOHOME, &args![ (_e("hello_powered", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop" => format!( "pagetop" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>", "<a href=\"{}\" target=\"_blank\">{}</a>",
"https://pagetop.cillero.es", "https://pagetop.cillero.es",
@ -83,7 +83,7 @@ fn hello_world() -> Container {
.with_component( .with_component(
Anchor::button( Anchor::button(
"https://github.com/manuelcillero/pagetop", "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_target(AnchorTarget::Blank)
.with_left_icon(Icon::with("git")) .with_left_icon(Icon::with("git"))
@ -92,7 +92,7 @@ fn hello_world() -> Container {
.with_component( .with_component(
Anchor::link( Anchor::link(
"#welcome", "#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_left_icon(Icon::with("arrow-down-circle-fill"))
.with_classes(ClassesOp::Add, "welcome-link"), .with_classes(ClassesOp::Add, "welcome-link"),
@ -111,11 +111,11 @@ fn welcome() -> Container {
.with_id("welcome") .with_id("welcome")
.with_classes(ClassesOp::Add, "welcome-col-text") .with_classes(ClassesOp::Add, "welcome-col-text")
.with_component(Heading::h2(html! { .with_component(Heading::h2(html! {
(t("welcome_page", Locale::From(&LOCALE_DEMOHOME))) (_t("welcome_page", Locale::From(&LOCALE_DEMOHOME)))
})) }))
.with_component( .with_component(
Heading::h3(html! { Heading::h3(html! {
(e("welcome_subtitle", Locale::With(&LOCALE_DEMOHOME, &args![ (_e("welcome_subtitle", Locale::With(&LOCALE_DEMOHOME, &args![
"app" => format!( "app" => format!(
"<span class=\"app-name\">{}</span>", "<span class=\"app-name\">{}</span>",
&config::SETTINGS.app.name &config::SETTINGS.app.name
@ -126,12 +126,12 @@ fn welcome() -> Container {
) )
.with_component( .with_component(
Paragraph::with(html! { Paragraph::with(html! {
(t("welcome_text1", Locale::From(&LOCALE_DEMOHOME))) (_t("welcome_text1", Locale::From(&LOCALE_DEMOHOME)))
}) })
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with( .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() grid::Column::new()
.with_classes(ClassesOp::Add, "pagetop-col-text") .with_classes(ClassesOp::Add, "pagetop-col-text")
.with_component(Heading::h2(html! { .with_component(Heading::h2(html! {
(t("pagetop_title", Locale::From(&LOCALE_DEMOHOME))) (_t("pagetop_title", Locale::From(&LOCALE_DEMOHOME)))
})) }))
.with_component( .with_component(
Paragraph::with(html! { Paragraph::with(html! {
(t("pagetop_text1", Locale::From(&LOCALE_DEMOHOME))) (_t("pagetop_text1", Locale::From(&LOCALE_DEMOHOME)))
}) })
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with(html! { .with_component(Paragraph::with(html! {
(t("pagetop_text2", Locale::From(&LOCALE_DEMOHOME))) (_t("pagetop_text2", Locale::From(&LOCALE_DEMOHOME)))
})) }))
.with_component(Paragraph::with(html! { .with_component(Paragraph::with(html! {
(e("pagetop_text3", Locale::With(&LOCALE_DEMOHOME, &args![ (_e("pagetop_text3", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop_website" => format!( "pagetop_website" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>", "<a href=\"{}\" target=\"_blank\">{}</a>",
"https://docs.rs/pagetop/latest/pagetop", "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() grid::Column::new()
.with_classes(ClassesOp::Add, "promo-col-text") .with_classes(ClassesOp::Add, "promo-col-text")
.with_component(Heading::h2(html! { .with_component(Heading::h2(html! {
(t("pagetop_promo_title", Locale::From(&LOCALE_DEMOHOME))) (_t("pagetop_promo_title", Locale::From(&LOCALE_DEMOHOME)))
})) }))
.with_component( .with_component(
Paragraph::with(html! { Paragraph::with(html! {
(e("pagetop_promo_text1", Locale::With(&LOCALE_DEMOHOME, &args![ (_e("pagetop_promo_text1", Locale::With(&LOCALE_DEMOHOME, &args![
"pagetop" => format!( "pagetop" => format!(
"<a href=\"{}\" target=\"_blank\">{}</a>", "<a href=\"{}\" target=\"_blank\">{}</a>",
"https://crates.io/crates/pagetop", "https://crates.io/crates/pagetop",
@ -216,16 +216,16 @@ fn reporting_issues() -> Container {
.with_classes(ClassesOp::Add, "reporting-col-text") .with_classes(ClassesOp::Add, "reporting-col-text")
.with_size(grid::ColumnSize::Is6of12) .with_size(grid::ColumnSize::Is6of12)
.with_component(Heading::h2(html! { .with_component(Heading::h2(html! {
(t("report_problems_title", Locale::From(&LOCALE_DEMOHOME))) (_t("report_problems_title", Locale::From(&LOCALE_DEMOHOME)))
})) }))
.with_component( .with_component(
Paragraph::with(html! { Paragraph::with(html! {
(t("report_problems_text1", Locale::From(&LOCALE_DEMOHOME))) (_t("report_problems_text1", Locale::From(&LOCALE_DEMOHOME)))
}) })
.with_display(ParagraphDisplay::Small), .with_display(ParagraphDisplay::Small),
) )
.with_component(Paragraph::with(html! { .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), Context(String),
} }
pub type AnchorIcon = ComponentsBundle; pub type AnchorIcon = ComponentArc;
#[rustfmt::skip] #[rustfmt::skip]
#[derive(Default)] #[derive(Default)]
@ -74,7 +74,7 @@ impl ComponentTrait for Anchor {
target=[target] target=[target]
{ {
(self.left_icon().render(rcx)) (self.left_icon().render(rcx))
span { (*self.html()) } (" ") span { (*self.html()) } (" ")
(self.right_icon().render(rcx)) (self.right_icon().render(rcx))
} }
} }
@ -158,15 +158,13 @@ impl Anchor {
#[fn_builder] #[fn_builder]
pub fn alter_left_icon(&mut self, icon: Icon) -> &mut Self { pub fn alter_left_icon(&mut self, icon: Icon) -> &mut Self {
self.left_icon.clear(); self.left_icon.replace(icon);
self.left_icon.add(icon);
self self
} }
#[fn_builder] #[fn_builder]
pub fn alter_right_icon(&mut self, icon: Icon) -> &mut Self { pub fn alter_right_icon(&mut self, icon: Icon) -> &mut Self {
self.right_icon.clear(); self.right_icon.replace(icon);
self.right_icon.add(icon);
self self
} }

View file

@ -12,10 +12,10 @@ impl ModuleTrait for Menu {
} }
fn name(&self) -> String { fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_MENU)) _t("module_name", Locale::From(&LOCALE_MENU))
} }
fn description(&self) -> Option<String> { 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 { fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_NODE)) _t("module_name", Locale::From(&LOCALE_NODE))
} }
fn description(&self) -> Option<String> { 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) { fn configure_service(&self, cfg: &mut server::web::ServiceConfig) {

View file

@ -15,11 +15,11 @@ impl ModuleTrait for User {
} }
fn name(&self) -> String { fn name(&self) -> String {
t("module_name", Locale::From(&LOCALE_USER)) _t("module_name", Locale::From(&LOCALE_USER))
} }
fn description(&self) -> Option<String> { 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> { fn dependencies(&self) -> Vec<ModuleStaticRef> {
@ -58,9 +58,9 @@ fn form_login() -> Form {
.with_element( .with_element(
form_element::Input::textfield() form_element::Input::textfield()
.with_name("name") .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( .with_help_text(
t( _t(
"username_help", "username_help",
Locale::With( Locale::With(
&LOCALE_USER, &LOCALE_USER,
@ -74,10 +74,10 @@ fn form_login() -> Form {
.with_element( .with_element(
form_element::Input::password() form_element::Input::password()
.with_name("pass") .with_name("pass")
.with_label(t("password", 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_help_text(_t("password_help", Locale::From(&LOCALE_USER)).as_str()),
) )
.with_element(form_element::Button::submit( .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" tracing-actix-web = "0.7.4"
fluent-templates = "0.8.0" fluent-templates = "0.8.0"
unic-langid = "0.9.1" unic-langid = { version = "0.9.1", features = ["macros"] }
actix-web = "4" actix-web = "4"
actix-session = { version = "0.7.2", features = ["cookie-session"] } 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::core::{module, module::ModuleStaticRef};
use crate::html::Markup; use crate::html::Markup;
use crate::response::fatal_error::FatalError;
use crate::response::page::ResultPage; use crate::response::page::ResultPage;
use crate::response::FatalError;
use crate::{config, locale, server, trace, LazyStatic}; use crate::{config, locale, server, trace, LazyStatic};
#[cfg(feature = "database")] #[cfg(feature = "database")]

View file

@ -1,6 +1,12 @@
mod context;
pub use context::{ContextOp, RenderContext};
mod definition; mod definition;
pub use definition::{component_mut, component_ref, AnyComponent, BaseComponent, ComponentTrait}; pub use definition::{component_mut, component_ref, AnyComponent, BaseComponent, ComponentTrait};
mod arc;
pub use arc::ComponentArc;
mod bundle; mod bundle;
pub use bundle::ComponentsBundle; 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::core::component::{ComponentArc, ComponentTrait, RenderContext};
use crate::html::{html, Markup, RenderContext}; use crate::html::{html, Markup};
use std::sync::{Arc, RwLock};
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct ComponentsBundle(Vec<Arc<RwLock<dyn ComponentTrait>>>); pub struct ComponentsBundle(Vec<ComponentArc>);
impl ComponentsBundle { impl ComponentsBundle {
pub fn new() -> Self { pub fn new() -> Self {
@ -12,13 +10,13 @@ impl ComponentsBundle {
} }
pub fn new_with(component: impl ComponentTrait) -> Self { pub fn new_with(component: impl ComponentTrait) -> Self {
let mut container = ComponentsBundle::new(); let mut bundle = ComponentsBundle::new();
container.add(component); bundle.add(component);
container bundle
} }
pub fn add(&mut self, component: impl ComponentTrait) { 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) { pub fn clear(&mut self) {
@ -27,10 +25,10 @@ impl ComponentsBundle {
pub fn render(&self, rcx: &mut RenderContext) -> Markup { pub fn render(&self, rcx: &mut RenderContext) -> Markup {
let mut components = self.0.clone(); let mut components = self.0.clone();
components.sort_by_key(|c| c.read().unwrap().weight()); components.sort_by_key(|c| c.weight());
html! { html! {
@for c in components.iter() { @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::core::module::{all::theme_by_single_name, ThemeStaticRef};
use crate::html::{html, Assets, IdentifierValue, JavaScript, Markup, StyleSheet}; use crate::html::{html, Assets, IdentifierValue, JavaScript, Markup, StyleSheet};
use crate::locale::{LanguageIdentifier, LANGID};
use crate::server::HttpRequest; use crate::server::HttpRequest;
use crate::{concat_string, config, util, LazyStatic}; use crate::{concat_string, config, util, LazyStatic};
@ -23,6 +24,7 @@ pub enum ContextOp {
#[rustfmt::skip] #[rustfmt::skip]
pub struct RenderContext { pub struct RenderContext {
language : &'static LanguageIdentifier,
theme : ThemeStaticRef, theme : ThemeStaticRef,
request : Option<HttpRequest>, request : Option<HttpRequest>,
stylesheets: Assets<StyleSheet>, stylesheets: Assets<StyleSheet>,
@ -35,6 +37,7 @@ impl Default for RenderContext {
#[rustfmt::skip] #[rustfmt::skip]
fn default() -> Self { fn default() -> Self {
RenderContext { RenderContext {
language : &LANGID,
theme : *DEFAULT_THEME, theme : *DEFAULT_THEME,
request : None, request : None,
stylesheets: Assets::<StyleSheet>::new(), stylesheets: Assets::<StyleSheet>::new(),
@ -81,6 +84,10 @@ impl RenderContext {
/// Context GETTERS. /// Context GETTERS.
pub(crate) fn language(&self) -> &LanguageIdentifier {
self.language
}
pub(crate) fn theme(&self) -> ThemeStaticRef { pub(crate) fn theme(&self) -> ThemeStaticRef {
self.theme 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::util::single_type_name;
use crate::Handle; use crate::{define_handle, Handle};
pub use std::any::Any as AnyComponent; pub use std::any::Any as AnyComponent;
define_handle!(COMPONENT_UNDEFINED);
pub trait BaseComponent { pub trait BaseComponent {
fn render(&mut self, rcx: &mut RenderContext) -> Markup; fn render(&mut self, rcx: &mut RenderContext) -> Markup;
} }
@ -13,7 +16,9 @@ pub trait ComponentTrait: AnyComponent + BaseComponent + Send + Sync {
where where
Self: Sized; Self: Sized;
fn handle(&self) -> Handle; fn handle(&self) -> Handle {
COMPONENT_UNDEFINED
}
fn name(&self) -> String { fn name(&self) -> String {
single_type_name::<Self>().to_owned() single_type_name::<Self>().to_owned()

View file

@ -1,5 +1,5 @@
use crate::core::component::{AnyComponent, ComponentTrait}; use crate::core::component::{AnyComponent, ComponentTrait, RenderContext};
use crate::html::{html, Markup, RenderContext}; use crate::html::{html, Markup};
use crate::{define_handle, Handle}; use crate::{define_handle, Handle};
define_handle!(HTML_MARKUP); 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; pub type IsRenderable = fn(&RenderContext) -> bool;

View file

@ -1,7 +1,7 @@
use super::ModuleTrait; use super::ModuleTrait;
use crate::core::component::{ComponentTrait, HtmlMarkup}; use crate::core::component::{ComponentTrait, RenderContext};
use crate::html::{html, Favicon, Markup, RenderContext}; use crate::html::{html, Favicon, Markup};
use crate::response::page::Page; use crate::response::page::Page;
use crate::{concat_string, config}; 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::stylesheet::{StyleSheet, TargetMedia};
pub use assets::Assets; pub use assets::Assets;
mod context;
pub use context::{ContextOp, RenderContext};
mod favicon; mod favicon;
pub use favicon::Favicon; pub use favicon::Favicon;

View file

@ -85,59 +85,12 @@ pub mod server;
// Tipos de respuestas a peticiones web. // Tipos de respuestas a peticiones web.
pub mod response; pub mod response;
// Funciones útiles. // Funciones útiles y macros declarativas.
pub mod util; pub mod util;
// Prepara y ejecuta la aplicación. // Prepara y ejecuta la aplicación.
pub mod app; 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. // RE-EXPORTA API ÚNICA.
// ************************************************************************************************* // *************************************************************************************************

View file

@ -92,34 +92,49 @@
//! ``` //! ```
use crate::html::{Markup, PreEscaped}; 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;
pub use fluent_templates::fluent_bundle::FluentValue; pub use fluent_templates::fluent_bundle::FluentValue;
pub use fluent_templates::{static_loader as static_locale, Loader, StaticLoader as Locales}; pub use fluent_templates::{static_loader as static_locale, Loader, StaticLoader as Locales};
pub use unic_langid::LanguageIdentifier;
use std::collections::HashMap; 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 /// Almacena el Identificador de Idioma Unicode
/// ([Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) /// ([Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier))
/// para la aplicación, obtenido de `SETTINGS.app.language`. /// para la aplicación, obtenido de `SETTINGS.app.language`.
pub static LANGID: LazyStatic<LanguageIdentifier> = pub static LANGID: LazyStatic<&LanguageIdentifier> =
LazyStatic::new(|| match config::SETTINGS.app.language.parse() { LazyStatic::new(
Ok(language) => language, || match LANGUAGES.get(config::SETTINGS.app.language.as_str()) {
Err(_) => { Some((langid, _)) => langid,
trace::warn!( _ => {
"{}, {} \"{}\"! {}, {}", trace::warn!(
"Failed to parse language", "{}, {} \"{}\"! {}, {}",
"unrecognized Unicode Language Identifier", "Failed to parse language",
config::SETTINGS.app.language, "unrecognized Unicode Language Identifier",
"Using \"en-US\"", config::SETTINGS.app.language,
"check the settings file", "Using \"en-US\"",
); "check the settings file",
"en-US".parse().unwrap() );
} &*DEFAULT_LANGID
}); }
},
);
#[macro_export] #[macro_export]
/// Define un conjunto de elementos de localización y funciones locales de traducción. /// 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::*; use $crate::locale::*;
static_locale! { static_locale! {
static $LOCALES = { pub static $LOCALES = {
locales: $dir_locales, locales: $dir_locales,
$( core_locales: $core_locales, )? $( core_locales: $core_locales, )?
fallback_language: "en-US", 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 { match locale {
Locale::From(locales) => locales.lookup(&LANGID, key).unwrap_or(key.to_string()), Locale::From(locales) => locales.lookup(&LANGID, key).unwrap_or(key.to_string()),
Locale::With(locales, args) => locales 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 { pub fn _e(key: &str, locale: Locale) -> Markup {
PreEscaped(t(key, locale)) 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;
pub use crate::server::HttpMessage; 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; pub use crate::app::Application;

View file

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

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::response::{page::Page, ResponseError};
use crate::server::http::{header::ContentType, StatusCode}; use crate::server::http::{header::ContentType, StatusCode};
use crate::server::{HttpRequest, HttpResponse}; use crate::server::{HttpRequest, HttpResponse};
@ -24,11 +29,10 @@ impl fmt::Display for FatalError {
FatalError::BadRequest(_) => write!(f, "Bad Client Data"), FatalError::BadRequest(_) => write!(f, "Bad Client Data"),
// Error 403. // Error 403.
FatalError::AccessDenied(request) => { FatalError::AccessDenied(request) => {
let mut error_page = Page::new(request.clone()); let error_page = Page::new(request.clone());
let error_content = error_page.context().theme().error_403_access_denied();
if let Ok(page) = error_page if let Ok(page) = error_page
.with_title("Error FORBIDDEN") .with_title("Error FORBIDDEN")
.with_this_in("region-content", error_content) .with_this_in("region-content", error403::Error403)
.with_template("error") .with_template("error")
.render() .render()
{ {
@ -39,11 +43,10 @@ impl fmt::Display for FatalError {
} }
// Error 404. // Error 404.
FatalError::NotFound(request) => { FatalError::NotFound(request) => {
let mut error_page = Page::new(request.clone()); let error_page = Page::new(request.clone());
let error_content = error_page.context().theme().error_404_not_found();
if let Ok(page) = error_page if let Ok(page) = error_page
.with_title("Error RESOURCE NOT FOUND") .with_title("Error RESOURCE NOT FOUND")
.with_this_in("region-content", error_content) .with_this_in("region-content", error404::Error404)
.with_template("error") .with_template("error")
.render() .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::component::*;
use crate::core::hook::{action_ref, run_actions}; use crate::core::hook::{action_ref, run_actions};
use crate::html::{ use crate::html::{html, AttributeValue, Classes, ClassesOp, Favicon, Markup, DOCTYPE};
html, AttributeValue, Classes, ClassesOp, ContextOp, Favicon, Markup, RenderContext, DOCTYPE, use crate::response::fatal_error::FatalError;
};
use crate::response::FatalError;
use crate::{config, fn_builder, locale, server, trace, LazyStatic}; use crate::{config, fn_builder, locale, server, trace, LazyStatic};
use std::collections::HashMap; use std::collections::HashMap;

View file

@ -1,7 +1,11 @@
//! Funciones útiles. //! Funciones útiles y macros declarativas.
use crate::Handle; use crate::Handle;
// *************************************************************************************************
// FUNCIONES ÚTILES.
// *************************************************************************************************
// https://stackoverflow.com/a/71464396 // https://stackoverflow.com/a/71464396
pub const fn handle( pub const fn handle(
module_path: &'static str, 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 { pub fn single_type_name<T: ?Sized>() -> &'static str {
partial_type_name(std::any::type_name::<T>(), 1) 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(),
);
}
}};
}