1use crate::utils::defaults::DEFAULT_TEXT_ITEM;
22use crate::utils::types::RUMString;
23use axum::extract::State;
24use phf::OrderedMap;
25pub use phf_macros::phf_ordered_map as rumtk_create_const_ordered_map;
26use serde::{Deserialize, Serialize};
27use std::collections::HashMap;
28use std::sync::{Arc, Mutex};
29
30pub type TextMap = HashMap<RUMString, RUMString>;
31pub type NestedTextMap = HashMap<RUMString, TextMap>;
32pub type NestedNestedTextMap = HashMap<RUMString, NestedTextMap>;
33pub type RootNestedNestedTextMap = HashMap<RUMString, NestedNestedTextMap>;
34pub type RootRootNestedNestedTextMap = HashMap<RUMString, RootNestedNestedTextMap>;
35
36pub type ConstTextMap = OrderedMap<&'static str, &'static str>;
37pub type ConstNestedTextMap = OrderedMap<&'static str, &'static ConstTextMap>;
38pub type ConstNestedNestedTextMap = OrderedMap<&'static str, &'static ConstNestedTextMap>;
39
40#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Default)]
48pub struct AppConf {
49 pub title: RUMString,
50 pub description: RUMString,
51 pub copyright: RUMString,
52 pub lang: RUMString,
53 pub theme: RUMString,
54 pub custom_css: bool,
55
56 strings: RootRootNestedNestedTextMap,
57 config: NestedNestedTextMap,
58 }
60
61impl AppConf {
62 pub fn update_site_info(
63 &mut self,
64 title: RUMString,
65 description: RUMString,
66 copyright: RUMString,
67 ) {
68 if !title.is_empty() {
69 self.title = title;
70 }
71 if !description.is_empty() {
72 self.description = description;
73 }
74 if !copyright.is_empty() {
75 self.copyright = copyright;
76 }
77 }
78
79 pub fn get_text(&self, item: &str) -> NestedNestedTextMap {
80 match self.strings.get(&self.lang) {
81 Some(l) => match l.get(item) {
82 Some(i) => i.clone(),
83 None => NestedNestedTextMap::default(),
84 },
85 None => NestedNestedTextMap::default(),
86 }
87 }
88
89 pub fn get_conf(&self, section: &str) -> TextMap {
90 match self.config.get(section) {
91 Some(l) => match l.get(&self.lang) {
92 Some(i) => i.clone(),
93 None => match l.get(DEFAULT_TEXT_ITEM) {
94 Some(i) => i.clone(),
95 None => TextMap::default(),
96 },
97 },
98 None => TextMap::default(),
99 }
100 }
101}
102
103pub type SharedAppConf = Arc<Mutex<AppConf>>;
104pub type RouterAppConf = State<Arc<Mutex<AppConf>>>;
105
106#[macro_export]
107macro_rules! rumtk_web_load_conf {
108 ( $args:expr ) => {{
109 rumtk_web_load_conf!($args, "./app.json")
110 }};
111 ( $args:expr, $path:expr ) => {{
112 use rumtk_core::strings::RUMStringConversions;
113 use rumtk_core::{rumtk_deserialize, rumtk_serialize};
114 use std::fs::read_to_string;
115 use std::sync::{Arc, Mutex};
116 use $crate::utils::AppConf;
117
118 let json = match read_to_string($path) {
119 Ok(json) => json,
120 Err(err) => rumtk_serialize!(AppConf::default())?,
121 };
122
123 let mut conf: AppConf = match rumtk_deserialize!(json) {
124 Ok(conf) => conf,
125 Err(err) => panic!(
126 "The App config file in {} does not meet the expected structure. \
127 See the documentation for more information. Error: {}\n{}",
128 $path, err, json
129 ),
130 };
131 conf.update_site_info(
132 $args.title.clone(),
133 $args.description.clone(),
134 $args.copyright.clone(),
135 );
136 Arc::new(Mutex::new(conf))
137 }};
138}
139
140#[macro_export]
141macro_rules! rumtk_web_get_string {
142 ( $conf:expr, $item:expr ) => {{
143 let owned_state = $conf.lock().expect("Lock failure");
144 owned_state.get_text($item)
145 }};
146}
147
148#[macro_export]
149macro_rules! rumtk_web_get_conf {
150 ( $conf:expr, $item:expr ) => {{
151 let owned_state = $conf.lock().expect("Lock failure");
152 owned_state.get_conf($item)
153 }};
154}
155
156pub const DEFAULT_TEXT: fn() -> RUMString = || RUMString::default();
160pub const DEFAULT_TEXTMAP: fn() -> TextMap = || TextMap::default();
161pub const DEFAULT_NESTEDTEXTMAP: fn() -> NestedTextMap = || NestedTextMap::default();
162pub const DEFAULT_NESTEDNESTEDTEXTMAP: fn() -> NestedNestedTextMap =
163 || NestedNestedTextMap::default();