use crate::jobs::{Job, JobID};
use crate::utils::defaults::DEFAULT_TEXT_ITEM;
use crate::utils::types::RUMString;
use askama::PrimitiveType;
use axum::extract::State;
use tower_http::cors::CorsLayer;
pub use phf_macros::phf_ordered_map as rumtk_create_const_ordered_map;
use crate::defaults::{DEFAULT_LANG_ITEM, DEFAULT_THEME_ITEM};
use crate::{NestedNestedTextMap, NestedTextMap, PipelineGroup, RootNestedNestedTextMap, TextMap};
use reqwest::header;
use rumtk_core::base::RUMVec;
use rumtk_core::net::tcp::SafeLock;
use rumtk_core::pipelines::pipeline_types::RUMCommandLine;
use rumtk_core::serde::{RUMDeJson, RUMSerJson};
use rumtk_core::types::RUMID;
use rumtk_core::types::{RUMHashMap, RUMOrderedMap};
use rumtk_core::{rumtk_generate_id, rumtk_new_lock};
#[derive(RUMSerJson, RUMDeJson, PartialEq, Debug, Clone, Default)]
pub struct FlagsConf {
pub custom_css: bool,
pub enable_icons: bool,
}
#[derive(RUMSerJson, RUMDeJson, PartialEq, Debug, Clone, Default)]
pub struct HeaderConf {
pub logo_source: Option<RUMString>,
pub icon_source: Option<RUMString>,
pub icon_type: Option<RUMString>,
pub disable_navlinks: bool,
pub disable_logo: bool,
}
#[derive(RUMSerJson, RUMDeJson, PartialEq, Debug, Clone, Default)]
pub struct FooterConf {
pub socials_list: RUMString,
pub disable_contact_button: bool,
}
#[derive(RUMSerJson, RUMDeJson, PartialEq, Debug, Clone, Default)]
pub struct PipelineConf {
pub settings: Option<TextMap>,
pub data_templates: Option<NestedTextMap>,
pub targets: Option<TextMap>,
pub categories: Option<RUMHashMap<RUMString, PipelineGroup>>
}
impl PipelineConf {
pub fn get_settings(&self) -> Option<&TextMap> {
self.settings.as_ref()
}
pub fn get_pipeline_category(&self, pipeline_category: &str) -> Option<&PipelineGroup> {
match self.categories {
Some(ref categories) => {
match categories.get(pipeline_category) {
Some(pipelines) => Some(pipelines),
None => None
}
}
None => None,
}
}
pub fn get_available_pipeline_names(&self) -> Vec<&RUMString> {
match self.targets.as_ref() {
Some(group) => {
let mut keys = group.keys().collect::<Vec<&RUMString>>();
keys.sort_unstable();
keys
},
None => vec![]
}
}
pub fn get_pipeline(&self, pipeline_category: &str, pipeline_name: &str) -> RUMCommandLine {
match self.get_pipeline_category(pipeline_category) {
Some(group) => match group.get(pipeline_name) {
Some(pipeline) => pipeline.to_owned(),
None => RUMCommandLine::new()
},
None => RUMCommandLine::new()
}
}
pub fn get_target(&self, profile: &str) -> RUMString {
match self.targets.as_ref() {
Some(targets) => match targets.get(profile) {
Some(pipeline) => pipeline.to_owned(),
None => RUMString::default()
},
None => RUMString::default()
}
}
pub fn get_template(&self, name: &str) -> Option<&TextMap> {
match self.data_templates.as_ref() {
Some(templates) => templates.get(name),
None => None
}
}
pub fn get_available_data_templates(&self) -> Vec<&RUMString> {
match self.data_templates.as_ref() {
Some(group) => {
let mut keys = group.keys().collect::<Vec<&RUMString>>();
keys.sort_unstable();
keys
},
None => vec![]
}
}
}
#[derive(RUMSerJson, RUMDeJson, PartialEq, Debug, Clone, Default)]
pub struct PageConf {
pub url: RUMString,
pub _static: bool,
}
pub type PageMap = RUMOrderedMap<RUMString, PageConf>;
#[derive(RUMSerJson, RUMDeJson, PartialEq, Debug, Clone, Default)]
pub struct RouterConf {
pub pages: Option<PageMap>,
pub redirect: Option<TextMap>,
pub service_routes: Option<NestedTextMap>,
}
impl RouterConf {
pub fn get_page(&self, name: &RUMString) -> Option<&PageConf> {
match &self.pages {
Some(pages) => pages.get(name),
None => None
}
}
pub fn get_redirect(&self, name: &RUMString) -> Option<&RUMString> {
match &self.redirect {
Some(redirects) => redirects.get(name),
None => None
}
}
pub fn get_service_route(&self, name: &RUMString) -> Option<TextMap> {
match &self.service_routes {
Some(service_routes) => Some(service_routes.get(name)?.clone()),
None => None
}
}
}
#[derive(RUMSerJson, RUMDeJson, PartialEq, Debug, Clone, Default)]
pub struct CORSConf {
pub origins: Option<RUMVec<RUMString>>,
pub methods: Option<RUMVec<RUMString>>,
pub headers: Option<RUMVec<RUMString>>,
pub allow_credentials: bool,
pub allow_private_network: bool,
}
impl CORSConf {
pub fn build_cors_layer(&self) -> CorsLayer {
use axum::http::HeaderValue;
use axum::http::{header, method};
let mut cors = CorsLayer::new();
match &self.origins {
Some(origins) => {
for origin in origins {
let o = origin.parse::<HeaderValue>().unwrap();
cors = cors.allow_origin(o);
}
},
None => {},
}
match &self.methods {
Some(methods) => {
let m: RUMVec<method::Method> = methods.iter().map(|method| method.parse::<method::Method>().unwrap()).collect();
cors = cors.allow_methods(m);
},
None => {}
}
match &self.headers {
Some(headers) => {
let h: RUMVec<header::HeaderName> = headers.iter().map(|header| header.parse::<header::HeaderName>().unwrap()).collect();
cors = cors.allow_headers(h);
},
None => {}
}
cors = cors.allow_credentials(self.allow_credentials);
if self.allow_credentials {
cors = cors.allow_headers([header::AUTHORIZATION, header::ACCEPT]);
}
cors = cors.allow_private_network(self.allow_private_network);
cors
}
}
#[derive(RUMSerJson, RUMDeJson, PartialEq, Debug, Clone)]
pub struct AppConf {
pub title: RUMString,
pub description: RUMString,
pub company: RUMString,
pub copyright: RUMString,
pub lang: RUMString,
pub theme: RUMString,
pub flags: FlagsConf,
pub header_conf: HeaderConf,
pub footer_conf: FooterConf,
strings: RootNestedNestedTextMap,
config: NestedNestedTextMap,
pipelines: PipelineConf,
pub router: RouterConf,
pub cors: Option<CORSConf>,
}
impl AppConf {
pub fn update_site_info(
&mut self,
title: RUMString,
description: RUMString,
company: RUMString,
copyright: RUMString,
) {
if !title.is_empty() {
self.title = title;
}
if !company.is_empty() {
self.company = company;
}
if !description.is_empty() {
self.description = description;
}
if !copyright.is_empty() {
self.copyright = copyright;
}
}
pub fn get_pipelines(&self) -> &PipelineConf {
&self.pipelines
}
pub fn get_text(&self, item: &str) -> NestedTextMap {
match self.strings.get(&self.lang) {
Some(l) => match l.get(item) {
Some(i) => i.clone(),
None => NestedTextMap::default(),
},
None => NestedTextMap::default(),
}
}
pub fn get_section(&self, section: &str) -> TextMap {
match self.config.get(&self.lang) {
Some(l) => match l.get(section) {
Some(i) => i.clone(),
None => self.get_default_item(section),
},
None => self.get_default_item(section),
}
}
pub fn get_default_item(&self, section: &str) -> TextMap {
match self.config.get(DEFAULT_TEXT_ITEM) {
Some(l) => match l.get(section) {
Some(i) => i.clone(),
None => TextMap::default(),
},
None => TextMap::default(),
}
}
}
impl Default for AppConf {
fn default() -> Self {
AppConf {
title: "".to_string(),
description: "".to_string(),
company: "".to_string(),
copyright: "".to_string(),
lang: DEFAULT_LANG_ITEM.to_string(),
theme: DEFAULT_THEME_ITEM.to_string(),
flags: FlagsConf::default(),
header_conf: HeaderConf::default(),
footer_conf: FooterConf::default(),
strings: RootNestedNestedTextMap::default(),
config: NestedNestedTextMap::default(),
pipelines: PipelineConf::default(),
router: RouterConf::default(),
cors: Some(CORSConf::default()),
}
}
}
pub type ClipboardID = RUMString;
#[derive(Default, Debug, Clone)]
pub struct AppState {
config: AppConf,
clipboard: NestedTextMap,
jobs: RUMHashMap<RUMID, Job>,
}
pub type SharedAppState = SafeLock<AppState>;
impl AppState {
pub fn new() -> AppState {
AppState {
config: AppConf::default(),
clipboard: NestedTextMap::default(),
jobs: RUMHashMap::default(),
}
}
pub fn new_safe() -> SharedAppState {
rumtk_new_lock!(AppState::new())
}
pub fn from_safe(conf: AppConf) -> SharedAppState {
rumtk_new_lock!(AppState::from(conf))
}
pub fn get_config(&self) -> &AppConf {
&self.config
}
pub fn get_config_mut(&mut self) -> &mut AppConf {
&mut self.config
}
pub fn has_clipboard(&self, id: &ClipboardID) -> bool {
self.clipboard.contains_key(id)
}
pub fn has_job(&self, id: &JobID) -> bool {
self.jobs.contains_key(id)
}
pub fn push_job_result(&mut self, id: &JobID, job: Job) {
self.jobs.insert(id.clone(), job);
}
pub fn push_to_clipboard(&mut self, data: TextMap) -> ClipboardID {
let clipboard_id = rumtk_generate_id!().to_string();
self.clipboard.insert(clipboard_id.clone(), data);
clipboard_id
}
pub fn request_clipboard_slice(&mut self) -> ClipboardID {
let clipboard_id = rumtk_generate_id!().to_string();
self.clipboard
.insert(clipboard_id.clone(), TextMap::default());
clipboard_id
}
pub fn pop_job(&mut self, id: &RUMID) -> Option<Job> {
self.jobs.remove(id)
}
pub fn pop_clipboard(&mut self, id: &ClipboardID) -> Option<TextMap> {
self.clipboard.shift_remove(id)
}
}
impl From<AppConf> for AppState {
fn from(config: AppConf) -> Self {
AppState {
config,
clipboard: NestedTextMap::default(),
jobs: RUMHashMap::default(),
}
}
}
pub type RouterAppState = State<SharedAppState>;
#[macro_export]
macro_rules! rumtk_web_load_conf {
( $args:expr ) => {{
use $crate::defaults::{DEFAULT_APP_CONFIG};
rumtk_web_load_conf!($args, DEFAULT_APP_CONFIG)
}};
( $args:expr, $path:expr ) => {{
use rumtk_core::rumtk_deserialize;
use rumtk_core::strings::RUMStringConversions;
use rumtk_core::types::RUMHashMap;
use $crate::AppConf;
use std::fs;
use $crate::rumtk_web_save_conf;
use $crate::utils::{AppState, TextMap};
let json = match fs::read_to_string($path) {
Ok(json) => json,
Err(err) => rumtk_web_save_conf!($path),
};
let mut conf: AppConf = match rumtk_deserialize!(&json) {
Ok(conf) => conf,
Err(err) => panic!(
"The App config file in {} does not meet the expected structure. \
See the documentation for more information. Error: {}\n{}",
$path, err, json
),
};
conf.update_site_info(
$args.title.clone(),
$args.description.clone(),
$args.company.clone(),
$args.copyright.clone(),
);
AppState::from_safe(conf)
}};
}
#[macro_export]
macro_rules! rumtk_web_save_conf {
( ) => {{
$crate::utils::defaults::DEFAULT_APP_CONFIG;
rumtk_web_save_conf!(DEFAULT_APP_CONFIG)
}};
( $path:expr ) => {{
use rumtk_core::rumtk_serialize;
use rumtk_core::strings::RUMStringConversions;
use std::fs;
use $crate::utils::AppConf;
let json = rumtk_serialize!(&AppConf::default()).unwrap_or_default();
fs::write($path, &json);
json
}};
}
#[macro_export]
macro_rules! rumtk_web_get_config_string {
( $conf:expr, $item:expr ) => {{
use $crate::rumtk_web_get_config;
use $crate::AppConf;
rumtk_web_get_config!($conf).get_text($item)
}};
}
#[macro_export]
macro_rules! rumtk_web_get_config_section {
( $conf:expr, $item:expr ) => {{
use $crate::rumtk_web_get_config;
use $crate::AppConf;
rumtk_web_get_config!($conf).get_section($item)
}};
}
#[macro_export]
macro_rules! rumtk_web_get_pipelines {
( $conf:expr ) => {{
use $crate::rumtk_web_get_config;
use $crate::AppConf;
rumtk_web_get_config!($conf).get_pipelines()
}};
}
#[macro_export]
macro_rules! rumtk_web_get_config {
( $state:expr ) => {{
use rumtk_core::{rumtk_lock_read};
rumtk_lock_read!($state.clone()).get_config()
}};
}
#[macro_export]
macro_rules! rumtk_web_set_config {
( $state:expr ) => {{
use rumtk_core::rumtk_lock_write;
rumtk_lock_write!($state.clone()).get_config_mut()
}};
}
#[macro_export]
macro_rules! rumtk_web_modify_state {
( $state:expr ) => {{
use rumtk_core::rumtk_lock_write;
rumtk_lock_write!($state.clone())
}};
}