1use crate::jobs::{Job, JobID};
24use crate::utils::defaults::DEFAULT_TEXT_ITEM;
25use crate::utils::types::RUMString;
26use axum::extract::State;
27use phf::OrderedMap;
28pub use phf_macros::phf_ordered_map as rumtk_create_const_ordered_map;
29use rumtk_core::rumtk_generate_id;
30use rumtk_core::strings::RUMStringConversions;
31use rumtk_core::types::{RUMDeserialize, RUMDeserializer, RUMSerialize, RUMSerializer, RUMID};
32use rumtk_core::types::{RUMHashMap, RUMOrderedMap};
33use std::sync::{Arc, RwLock};
34
35pub type TextMap = RUMOrderedMap<RUMString, RUMString>;
36pub type NestedTextMap = RUMOrderedMap<RUMString, TextMap>;
37pub type NestedNestedTextMap = RUMOrderedMap<RUMString, NestedTextMap>;
38pub type RootNestedNestedTextMap = RUMOrderedMap<RUMString, NestedNestedTextMap>;
39
40pub type ConstTextMap = OrderedMap<&'static str, &'static str>;
41pub type ConstNestedTextMap = OrderedMap<&'static str, &'static ConstTextMap>;
42pub type ConstNestedNestedTextMap = OrderedMap<&'static str, &'static ConstNestedTextMap>;
43
44#[derive(RUMSerialize, RUMDeserialize, PartialEq, Debug, Clone, Default)]
45pub struct HeaderConf {
46 pub logo_size: RUMString,
47 pub disable_navlinks: bool,
48 pub disable_logo: bool,
49}
50
51#[derive(RUMSerialize, RUMDeserialize, PartialEq, Debug, Clone, Default)]
52pub struct FooterConf {
53 pub socials_list: RUMString,
54 pub disable_contact_button: bool,
55}
56
57#[derive(RUMSerialize, RUMDeserialize, PartialEq, Debug, Clone, Default)]
65pub struct AppConf {
66 pub title: RUMString,
67 pub description: RUMString,
68 pub company: RUMString,
69 pub copyright: RUMString,
70 pub lang: RUMString,
71 pub theme: RUMString,
72 pub custom_css: bool,
73 pub header_conf: HeaderConf,
74 pub footer_conf: FooterConf,
75
76 strings: RootNestedNestedTextMap,
77 config: NestedNestedTextMap,
78 }
80
81impl AppConf {
82 pub fn update_site_info(
83 &mut self,
84 title: RUMString,
85 description: RUMString,
86 company: RUMString,
87 copyright: RUMString,
88 ) {
89 if !title.is_empty() {
90 self.title = title;
91 }
92 if !company.is_empty() {
93 self.company = company;
94 }
95 if !description.is_empty() {
96 self.description = description;
97 }
98 if !copyright.is_empty() {
99 self.copyright = copyright;
100 }
101 }
102
103 pub fn get_text(&self, item: &str) -> NestedTextMap {
104 match self.strings.get(&self.lang) {
105 Some(l) => match l.get(item) {
106 Some(i) => i.clone(),
107 None => NestedTextMap::default(),
108 },
109 None => NestedTextMap::default(),
110 }
111 }
112
113 pub fn get_conf(&self, section: &str) -> TextMap {
114 match self.config.get(&self.lang) {
115 Some(l) => match l.get(section) {
116 Some(i) => i.clone(),
117 None => match self.config.get(DEFAULT_TEXT_ITEM) {
118 Some(l) => match l.get(section) {
119 Some(i) => i.clone(),
120 None => TextMap::default(),
121 },
122 None => TextMap::default(),
123 },
124 },
125 None => match self.config.get(DEFAULT_TEXT_ITEM) {
126 Some(l) => match l.get(section) {
127 Some(i) => i.clone(),
128 None => TextMap::default(),
129 },
130 None => TextMap::default(),
131 },
132 }
133 }
134}
135
136pub type ClipboardID = RUMString;
137pub struct AppState {
143 config: AppConf,
144 clipboard: NestedTextMap,
145 jobs: RUMHashMap<RUMID, Job>,
146}
147
148pub type SafeAppState = Arc<RwLock<AppState>>;
149
150impl AppState {
151 pub fn new() -> AppState {
152 AppState {
153 config: AppConf::default(),
154 clipboard: NestedTextMap::default(),
155 jobs: RUMHashMap::default(),
156 }
157 }
158
159 pub fn new_safe() -> SafeAppState {
160 Arc::new(RwLock::new(AppState::new()))
161 }
162
163 pub fn from_safe(conf: AppConf) -> SafeAppState {
164 Arc::new(RwLock::new(AppState::from(conf)))
165 }
166
167 pub fn get_config(&self) -> &AppConf {
168 &self.config
169 }
170
171 pub fn get_config_mut(&mut self) -> &mut AppConf {
172 &mut self.config
173 }
174
175 pub fn has_clipboard(&self, id: &ClipboardID) -> bool {
176 self.clipboard.contains_key(id)
177 }
178
179 pub fn has_job(&self, id: &JobID) -> bool {
180 self.jobs.contains_key(id)
181 }
182
183 pub fn push_job_result(&mut self, id: &JobID, job: Job) {
184 self.jobs.insert(id.clone(), job);
185 }
186
187 pub fn push_to_clipboard(&mut self, data: TextMap) -> ClipboardID {
188 let clipboard_id = rumtk_generate_id!().to_rumstring();
189 self.clipboard.insert(clipboard_id.clone(), data);
190 clipboard_id
191 }
192
193 pub fn request_clipboard_slice(&mut self) -> ClipboardID {
194 let clipboard_id = rumtk_generate_id!().to_rumstring();
195 self.clipboard
196 .insert(clipboard_id.clone(), TextMap::default());
197 clipboard_id
198 }
199
200 pub fn pop_job(&mut self, id: &RUMID) -> Option<Job> {
201 self.jobs.remove(id)
202 }
203
204 pub fn pop_clipboard(&mut self, id: &ClipboardID) -> Option<TextMap> {
205 self.clipboard.shift_remove(id)
206 }
207}
208
209impl From<AppConf> for AppState {
210 fn from(config: AppConf) -> Self {
211 AppState {
212 config,
213 clipboard: NestedTextMap::default(),
214 jobs: RUMHashMap::default(),
215 }
216 }
217}
218
219pub type SharedAppState = Arc<RwLock<AppState>>;
220pub type RouterAppState = State<Arc<RwLock<AppState>>>;
221
222#[macro_export]
223macro_rules! rumtk_web_load_conf {
224 ( $args:expr ) => {{
225 rumtk_web_load_conf!($args, "./app.json")
226 }};
227 ( $args:expr, $path:expr ) => {{
228 use rumtk_core::rumtk_deserialize;
229 use rumtk_core::strings::RUMStringConversions;
230 use rumtk_core::types::RUMHashMap;
231 use std::fs;
232
233 use $crate::rumtk_web_save_conf;
234 use $crate::utils::{AppConf, AppState, TextMap};
235
236 let json = match fs::read_to_string($path) {
237 Ok(json) => json,
238 Err(err) => rumtk_web_save_conf!($path),
239 };
240
241 let mut conf: AppConf = match rumtk_deserialize!(json) {
242 Ok(conf) => conf,
243 Err(err) => panic!(
244 "The App config file in {} does not meet the expected structure. \
245 See the documentation for more information. Error: {}\n{}",
246 $path, err, json
247 ),
248 };
249 conf.update_site_info(
250 $args.title.clone(),
251 $args.description.clone(),
252 $args.company.clone(),
253 $args.copyright.clone(),
254 );
255 AppState::from_safe(conf)
256 }};
257}
258
259#[macro_export]
260macro_rules! rumtk_web_save_conf {
261 ( $path:expr ) => {{
262 use rumtk_core::rumtk_serialize;
263 use rumtk_core::strings::RUMStringConversions;
264 use std::fs;
265 use $crate::utils::AppConf;
266
267 let json = rumtk_serialize!(AppConf::default(), true)?;
268 fs::write($path, &json);
269 json
270 }};
271}
272
273#[macro_export]
274macro_rules! rumtk_web_get_string {
275 ( $conf:expr, $item:expr ) => {{
276 let owned_state = $conf.read().expect("Lock failure");
277 owned_state.get_config().get_text($item)
278 }};
279}
280
281#[macro_export]
282macro_rules! rumtk_web_get_conf {
283 ( $conf:expr, $item:expr ) => {{
284 let owned_state = $conf.read().expect("Lock failure");
285 owned_state.get_config().get_conf($item)
286 }};
287}
288
289pub const DEFAULT_TEXT: fn() -> RUMString = || RUMString::default();
293pub const DEFAULT_TEXTMAP: fn() -> TextMap = || TextMap::default();
294pub const DEFAULT_NESTEDTEXTMAP: fn() -> NestedTextMap = || NestedTextMap::default();
295pub const DEFAULT_NESTEDNESTEDTEXTMAP: fn() -> NestedNestedTextMap =
296 || NestedNestedTextMap::default();