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