use crate::base::component::add_base_assets; use crate::concat_string; use crate::core::component::AnyOp; use crate::core::theme::all::{theme_by_short_name, THEME_DEFAULT}; use crate::core::theme::{ComponentsInRegions, ThemeRef}; use crate::html::{html, Markup}; use crate::html::{Assets, HeadScript, HeadStyles, JavaScript, StyleSheet}; use crate::locale::{LanguageIdentifier, LANGID_DEFAULT}; use crate::service::HttpRequest; use crate::util::TypeInfo; use std::collections::HashMap; use std::error::Error; use std::fmt; use std::str::FromStr; pub enum AssetsOp { LangId(&'static LanguageIdentifier), Theme(&'static str), Layout(&'static str), // Stylesheets. AddStyleSheet(StyleSheet), RemoveStyleSheet(&'static str), // Styles in head. AddHeadStyles(HeadStyles), RemoveHeadStyles(&'static str), // JavaScripts. AddJavaScript(JavaScript), RemoveJavaScript(&'static str), // Scripts in head. AddHeadScript(HeadScript), RemoveHeadScript(&'static str), // Add assets to properly use base components. AddBaseAssets, } #[derive(Debug)] pub enum ParamError { NotFound, ParseError(String), } impl fmt::Display for ParamError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ParamError::NotFound => write!(f, "Parameter not found"), ParamError::ParseError(e) => write!(f, "Parse error: {}", e), } } } impl Error for ParamError {} #[rustfmt::skip] pub struct Context { request : HttpRequest, langid : &'static LanguageIdentifier, theme : ThemeRef, layout : &'static str, stylesheet : Assets, // Stylesheets. headstyles : Assets, // Styles in head. javascript : Assets, // JavaScripts. headscript : Assets, // Scripts in head. regions : ComponentsInRegions, params : HashMap<&'static str, String>, id_counter : usize, } impl Context { #[rustfmt::skip] pub(crate) fn new(request: HttpRequest) -> Self { Context { request, langid : &LANGID_DEFAULT, theme : *THEME_DEFAULT, layout : "default", stylesheet : Assets::::new(), // Stylesheets. headstyles : Assets::::new(), // Styles in head. javascript : Assets::::new(), // JavaScripts. headscript : Assets::::new(), // Scripts in head. regions : ComponentsInRegions::default(), params : HashMap::<&str, String>::new(), id_counter : 0, } } #[rustfmt::skip] pub fn set_assets(&mut self, op: AssetsOp) -> &mut Self { match op { AssetsOp::LangId(langid) => { self.langid = langid; } AssetsOp::Theme(theme_name) => { self.theme = theme_by_short_name(theme_name).unwrap_or(*THEME_DEFAULT); } AssetsOp::Layout(layout) => { self.layout = layout; } // Stylesheets. AssetsOp::AddStyleSheet(css) => { self.stylesheet.add(css); } AssetsOp::RemoveStyleSheet(path) => { self.stylesheet.remove(path); } // Styles in head. AssetsOp::AddHeadStyles(styles) => { self.headstyles.add(styles); } AssetsOp::RemoveHeadStyles(path) => { self.headstyles.remove(path); } // JavaScripts. AssetsOp::AddJavaScript(js) => { self.javascript.add(js); } AssetsOp::RemoveJavaScript(path) => { self.javascript.remove(path); } // Scripts in head. AssetsOp::AddHeadScript(script) => { self.headscript.add(script); } AssetsOp::RemoveHeadScript(path) => { self.headscript.remove(path); } // Add assets to properly use base components. AssetsOp::AddBaseAssets => { add_base_assets(self); } } self } pub fn set_regions(&mut self, region: &'static str, op: AnyOp) -> &mut Self { self.regions.set_components(region, op); self } pub fn set_param(&mut self, key: &'static str, value: T) -> &mut Self { self.params.insert(key, value.to_string()); self } pub fn remove_param(&mut self, key: &'static str) -> &mut Self { self.params.remove(key); self } /// Context GETTERS. pub fn request(&self) -> &HttpRequest { &self.request } pub fn langid(&self) -> &LanguageIdentifier { self.langid } pub fn theme(&self) -> ThemeRef { self.theme } pub fn layout(&self) -> &str { self.layout } pub fn regions(&self) -> &ComponentsInRegions { &self.regions } pub fn get_param(&self, key: &'static str) -> Result { match self.params.get(key) { Some(value) => T::from_str(value).map_err(|_| ParamError::ParseError(value.clone())), None => Err(ParamError::NotFound), } } /// Context PREPARE. pub(crate) fn prepare_assets(&mut self) -> Markup { html! { (self.stylesheet.prepare()) // Stylesheets. (self.headstyles.prepare()) // Styles in head. (self.javascript.prepare()) // JavaScripts. (self.headscript.prepare()) // Scripts in head. } } pub(crate) fn prepare_region(&mut self, region: impl Into) -> Markup { self.regions .all_components(self.theme, region.into().as_str()) .render(self) } // Context EXTRAS. pub fn required_id(&mut self, id: Option) -> String { match id { Some(id) => id, None => { let prefix = TypeInfo::ShortName .of::() .trim() .replace(' ', "_") .to_lowercase(); let prefix = if prefix.is_empty() { "prefix".to_owned() } else { prefix }; self.id_counter += 1; concat_string!(prefix, "-", self.id_counter.to_string()) } } } }