Skip to main content

teaql_core/
web.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{BaseEntity, BaseEntityData, Entity, Record, SmartList, Value, record_to_json_value};
4
5pub const STYLE_KEY: &str = "style";
6pub const ACTION_LIST_KEY: &str = "actionList";
7pub const WEB_RESPONSE_VERSION: &str = "1.001";
8
9#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct WebStyle {
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub background_color: Option<String>,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub color: Option<String>,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub class_names: Option<String>,
18}
19
20impl WebStyle {
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    pub fn with_background_color(color: impl Into<String>) -> Self {
26        Self::new().background_color(color)
27    }
28
29    pub fn with_font_color(color: impl Into<String>) -> Self {
30        Self::new().font_color(color)
31    }
32
33    pub fn with_class_names(class_names: impl Into<String>) -> Self {
34        Self::new().class_names(class_names)
35    }
36
37    pub fn background_color(mut self, color: impl Into<String>) -> Self {
38        self.background_color = Some(color.into());
39        self
40    }
41
42    pub fn font_color(mut self, color: impl Into<String>) -> Self {
43        self.color = Some(color.into());
44        self
45    }
46
47    pub fn class_names(mut self, class_names: impl Into<String>) -> Self {
48        self.class_names = Some(class_names.into());
49        self
50    }
51
52    pub fn to_json_value(&self) -> serde_json::Value {
53        serde_json::to_value(self).expect("WebStyle serialization cannot fail")
54    }
55
56    pub fn bind_base(&self, entity: &mut BaseEntityData) {
57        entity.put_dynamic(STYLE_KEY, self.to_json_value());
58    }
59
60    pub fn bind_entity<E>(&self, entity: &mut E)
61    where
62        E: BaseEntity,
63    {
64        entity.put_dynamic(STYLE_KEY, self.to_json_value());
65    }
66
67    pub fn bind_record(&self, record: &mut Record) {
68        record.insert(STYLE_KEY.to_owned(), Value::Json(self.to_json_value()));
69    }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74pub struct WebAction {
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub key: Option<String>,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub name: Option<String>,
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub level: Option<String>,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub execute: Option<String>,
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub target: Option<String>,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub component: Option<String>,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub warning_message: Option<String>,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub role_for_list: Option<String>,
91    #[serde(rename = "requestURL", skip_serializing_if = "Option::is_none")]
92    pub request_url: Option<String>,
93}
94
95impl WebAction {
96    pub fn new() -> Self {
97        Self::default()
98    }
99
100    pub fn view_web_action() -> Self {
101        Self::new()
102            .name("VIEW DETAIL")
103            .level("view")
104            .execute("switchview")
105            .target("detail")
106    }
107
108    pub fn view_sub_list_action(
109        name: impl Into<String>,
110        list_view_name: impl Into<String>,
111        role_for_list: impl Into<String>,
112    ) -> Self {
113        Self::new()
114            .name(name)
115            .level("view")
116            .execute("gotoList")
117            .role_for_list(role_for_list)
118            .target(list_view_name)
119    }
120
121    pub fn simple_component_action(
122        name: impl Into<String>,
123        component_name: impl Into<String>,
124    ) -> Self {
125        Self::new().name(name).component(component_name)
126    }
127
128    pub fn modify_web_action(name: impl Into<String>, url: impl Into<String>) -> Self {
129        Self::modify_web_action_with_warning(name, url, None::<String>)
130    }
131
132    pub fn modify_web_action_with_warning(
133        name: impl Into<String>,
134        url: impl Into<String>,
135        warning_message: Option<impl Into<String>>,
136    ) -> Self {
137        let name = name.into();
138        Self::new()
139            .name(name.clone())
140            .key(name)
141            .level("modify")
142            .execute("switchview")
143            .target("modify")
144            .request_url(url)
145            .optional_warning_message(warning_message)
146    }
147
148    pub fn default_modify_web_action() -> Self {
149        Self::new()
150            .name("UPDATE")
151            .level("modify")
152            .execute("switchview")
153            .target("modify")
154    }
155
156    pub fn delete_web_action() -> Self {
157        Self::new()
158            .name("DELETE")
159            .level("delete")
160            .execute("switchview")
161            .target("deleteview")
162    }
163
164    pub fn delete_web_action_with_warning(
165        url: impl Into<String>,
166        warning_message: impl Into<String>,
167    ) -> Self {
168        Self::modify_web_action_with_warning("web.action.delete", url, Some(warning_message.into()))
169    }
170
171    pub fn audit_web_action(url: impl Into<String>, warning_message: impl Into<String>) -> Self {
172        Self::modify_web_action_with_warning("AUDIT", url, Some(warning_message.into()))
173    }
174
175    pub fn discard_web_action(url: impl Into<String>, warning_message: impl Into<String>) -> Self {
176        Self::modify_web_action_with_warning("DISCARD", url, Some(warning_message.into()))
177    }
178
179    pub fn goto_action(
180        name: impl Into<String>,
181        target: impl Into<String>,
182        url: impl Into<String>,
183    ) -> Self {
184        Self::new()
185            .name(name)
186            .level("modify")
187            .execute("gotoview")
188            .target(target)
189            .request_url(url)
190    }
191
192    pub fn switch_view_action(view_name: impl Into<String>, target: impl Into<String>) -> Self {
193        Self::new()
194            .name(view_name)
195            .level("modify")
196            .execute("switchview")
197            .target(target)
198    }
199
200    pub fn add_new_web_action(object_display_name: impl Into<String>) -> Self {
201        Self::new()
202            .name(format!("NEW {}", object_display_name.into()))
203            .level("modify")
204            .execute("switchview")
205            .target("addnew")
206    }
207
208    pub fn batch_upload_web_action() -> Self {
209        Self::new()
210            .name("BATCH UPLOAD")
211            .level("modify")
212            .execute("switchview")
213            .target("batchupload")
214    }
215
216    pub fn common_web_actions() -> Vec<Self> {
217        vec![Self::view_web_action(), Self::default_modify_web_action()]
218    }
219
220    pub fn key(mut self, key: impl Into<String>) -> Self {
221        self.key = Some(key.into());
222        self
223    }
224
225    pub fn name(mut self, name: impl Into<String>) -> Self {
226        self.name = Some(name.into());
227        self
228    }
229
230    pub fn level(mut self, level: impl Into<String>) -> Self {
231        self.level = Some(level.into());
232        self
233    }
234
235    pub fn execute(mut self, execute: impl Into<String>) -> Self {
236        self.execute = Some(execute.into());
237        self
238    }
239
240    pub fn target(mut self, target: impl Into<String>) -> Self {
241        self.target = Some(target.into());
242        self
243    }
244
245    pub fn component(mut self, component: impl Into<String>) -> Self {
246        self.component = Some(component.into());
247        self
248    }
249
250    pub fn warning_message(mut self, warning_message: impl Into<String>) -> Self {
251        self.warning_message = Some(warning_message.into());
252        self
253    }
254
255    pub fn optional_warning_message(mut self, warning_message: Option<impl Into<String>>) -> Self {
256        self.warning_message = warning_message.map(Into::into);
257        self
258    }
259
260    pub fn role_for_list(mut self, role_for_list: impl Into<String>) -> Self {
261        self.role_for_list = Some(role_for_list.into());
262        self
263    }
264
265    pub fn request_url(mut self, request_url: impl Into<String>) -> Self {
266        self.request_url = Some(request_url.into());
267        self
268    }
269
270    pub fn to_json_value(&self) -> serde_json::Value {
271        serde_json::to_value(self).expect("WebAction serialization cannot fail")
272    }
273
274    pub fn bind_base(&self, entity: &mut BaseEntityData) {
275        append_action(&mut entity.dynamic, self.to_json_value());
276    }
277
278    pub fn bind_entity<E>(&self, entity: &mut E)
279    where
280        E: BaseEntity,
281    {
282        append_action(&mut entity.base_mut().dynamic, self.to_json_value());
283    }
284
285    pub fn bind_record(&self, record: &mut Record) {
286        append_record_action(record, self.to_json_value());
287    }
288}
289
290#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
291#[serde(rename_all = "camelCase")]
292pub struct WebResponse {
293    pub data: Vec<serde_json::Value>,
294    pub result_code: i32,
295    #[serde(skip_serializing_if = "Option::is_none")]
296    pub status: Option<String>,
297    #[serde(skip_serializing_if = "Option::is_none")]
298    pub message: Option<String>,
299    pub record_count: u64,
300    pub version: String,
301}
302
303impl WebResponse {
304    pub fn success() -> Self {
305        Self {
306            data: Vec::new(),
307            result_code: 0,
308            status: Some("YES".to_owned()),
309            message: None,
310            record_count: 0,
311            version: WEB_RESPONSE_VERSION.to_owned(),
312        }
313    }
314
315    pub fn fail(message: impl Into<String>) -> Self {
316        Self {
317            data: Vec::new(),
318            result_code: 1,
319            status: Some("NO".to_owned()),
320            message: Some(message.into()),
321            record_count: 0,
322            version: WEB_RESPONSE_VERSION.to_owned(),
323        }
324    }
325
326    pub fn empty_list(message: impl Into<String>) -> Self {
327        Self {
328            data: Vec::new(),
329            result_code: 0,
330            status: None,
331            message: Some(message.into()),
332            record_count: 0,
333            version: WEB_RESPONSE_VERSION.to_owned(),
334        }
335    }
336
337    pub fn from_records(records: impl IntoIterator<Item = Record>) -> Self {
338        let data: Vec<_> = records
339            .into_iter()
340            .map(|record| record_to_json_value(&record))
341            .collect();
342        Self::success().with_data(data)
343    }
344
345    pub fn from_entity<E>(entity: &E) -> Self
346    where
347        E: Entity + Clone,
348    {
349        Self::from_records([entity.clone().into_record()])
350    }
351
352    pub fn from_entities<E>(entities: impl IntoIterator<Item = E>) -> Self
353    where
354        E: Entity,
355    {
356        Self::from_records(entities.into_iter().map(Entity::into_record))
357    }
358
359    pub fn from_smart_list<E>(smart_list: SmartList<E>) -> Self
360    where
361        E: Entity,
362    {
363        let total_count = smart_list.total_count_or_len();
364        Self::from_entities(smart_list).with_record_count(total_count)
365    }
366
367    pub fn with_data(mut self, data: Vec<serde_json::Value>) -> Self {
368        self.record_count = data.len() as u64;
369        self.data = data;
370        self
371    }
372
373    pub fn with_record_count(mut self, record_count: u64) -> Self {
374        self.record_count = record_count;
375        self
376    }
377
378    pub fn push_json(mut self, value: impl Into<serde_json::Value>) -> Self {
379        self.data.push(value.into());
380        self.record_count = self.data.len() as u64;
381        self
382    }
383
384    pub fn to_json_value(&self) -> serde_json::Value {
385        serde_json::to_value(self).expect("WebResponse serialization cannot fail")
386    }
387}
388
389fn append_action(
390    dynamic: &mut std::collections::BTreeMap<String, Value>,
391    action: serde_json::Value,
392) {
393    match dynamic.get_mut(ACTION_LIST_KEY) {
394        Some(Value::Json(serde_json::Value::Array(actions))) => actions.push(action),
395        Some(existing) => {
396            let previous = std::mem::replace(existing, Value::Null);
397            *existing = Value::Json(serde_json::Value::Array(vec![
398                previous.to_json_value(),
399                action,
400            ]));
401        }
402        None => {
403            dynamic.insert(
404                ACTION_LIST_KEY.to_owned(),
405                Value::Json(serde_json::Value::Array(vec![action])),
406            );
407        }
408    }
409}
410
411fn append_record_action(record: &mut Record, action: serde_json::Value) {
412    match record.get_mut(ACTION_LIST_KEY) {
413        Some(Value::Json(serde_json::Value::Array(actions))) => actions.push(action),
414        Some(existing) => {
415            let previous = std::mem::replace(existing, Value::Null);
416            *existing = Value::Json(serde_json::Value::Array(vec![
417                previous.to_json_value(),
418                action,
419            ]));
420        }
421        None => {
422            record.insert(
423                ACTION_LIST_KEY.to_owned(),
424                Value::Json(serde_json::Value::Array(vec![action])),
425            );
426        }
427    }
428}