pagetop/src/core/component/context.rs

201 lines
6.4 KiB
Rust

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<StyleSheet>, // Stylesheets.
headstyles : Assets<HeadStyles>, // Styles in head.
javascript : Assets<JavaScript>, // JavaScripts.
headscript : Assets<HeadScript>, // 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::<StyleSheet>::new(), // Stylesheets.
headstyles : Assets::<HeadStyles>::new(), // Styles in head.
javascript : Assets::<JavaScript>::new(), // JavaScripts.
headscript : Assets::<HeadScript>::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<T: FromStr + ToString>(&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<T: FromStr + ToString>(&self, key: &'static str) -> Result<T, ParamError> {
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<String>) -> Markup {
self.regions
.all_components(self.theme, region.into().as_str())
.render(self)
}
// Context EXTRAS.
pub fn required_id<T>(&mut self, id: Option<String>) -> String {
match id {
Some(id) => id,
None => {
let prefix = TypeInfo::ShortName
.of::<T>()
.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())
}
}
}
}