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 pub facets: BTreeMap<String, serde_json::Value>,
303}
304
305impl WebResponse {
306 pub fn success() -> Self {
307 Self {
308 data: Vec::new(),
309 result_code: 0,
310 status: Some("YES".to_owned()),
311 message: None,
312 record_count: 0,
313 version: WEB_RESPONSE_VERSION.to_owned(),
314 facets: BTreeMap::new(),
315 }
316 }
317
318 pub fn fail(message: impl Into<String>) -> Self {
319 Self {
320 data: Vec::new(),
321 result_code: 1,
322 status: Some("NO".to_owned()),
323 message: Some(message.into()),
324 record_count: 0,
325 version: WEB_RESPONSE_VERSION.to_owned(),
326 facets: BTreeMap::new(),
327 }
328 }
329
330 pub fn empty_list(message: impl Into<String>) -> Self {
331 Self {
332 data: Vec::new(),
333 result_code: 0,
334 status: None,
335 message: Some(message.into()),
336 record_count: 0,
337 version: WEB_RESPONSE_VERSION.to_owned(),
338 facets: BTreeMap::new(),
339 }
340 }
341
342 pub fn from_records(records: impl IntoIterator<Item = Record>) -> Self {
343 let data: Vec<_> = records
344 .into_iter()
345 .map(|record| record_to_json_value(&record))
346 .collect();
347 Self::success().with_data(data)
348 }
349
350 pub fn from_entity<E>(entity: &E) -> Self
351 where
352 E: Entity + Clone,
353 {
354 Self::from_records([entity.clone().into_record()])
355 }
356
357 pub fn from_entities<E>(entities: impl IntoIterator<Item = E>) -> Self
358 where
359 E: Entity,
360 {
361 Self::from_records(entities.into_iter().map(Entity::into_record))
362 }
363
364 pub fn from_smart_list<E>(mut smart_list: SmartList<E>) -> Self
365 where
366 E: Entity,
367 {
368 let total_count = smart_list.total_count_or_len();
369 let mut facets = BTreeMap::new();
370 for (key, facet_list) in smart_list.take_facets() {
371 let data: Vec<_> = facet_list
372 .data
373 .iter()
374 .map(|record| record_to_json_value(record))
375 .collect();
376 facets.insert(key, serde_json::Value::Array(data));
377 }
378 Self::from_entities(smart_list)
379 .with_record_count(total_count)
380 .with_facets(facets)
381 }
382
383 pub fn with_data(mut self, data: Vec<serde_json::Value>) -> Self {
384 self.record_count = data.len() as u64;
385 self.data = data;
386 self
387 }
388
389 pub fn with_record_count(mut self, record_count: u64) -> Self {
390 self.record_count = record_count;
391 self
392 }
393
394 pub fn with_facets(mut self, facets: BTreeMap<String, serde_json::Value>) -> Self {
395 self.facets = facets;
396 self
397 }
398
399 pub fn with_facets_option(mut self, facets: Option<BTreeMap<String, serde_json::Value>>) -> Self {
400 self.facets = facets.unwrap_or_default();
401 self
402 }
403
404 pub fn push_json(mut self, value: impl Into<serde_json::Value>) -> Self {
405 self.data.push(value.into());
406 self.record_count = self.data.len() as u64;
407 self
408 }
409
410 pub fn to_json_value(&self) -> serde_json::Value {
411 serde_json::to_value(self).expect("WebResponse serialization cannot fail")
412 }
413}
414
415fn append_action(
416 dynamic: &mut std::collections::BTreeMap<String, Value>,
417 action: serde_json::Value,
418) {
419 match dynamic.get_mut(ACTION_LIST_KEY) {
420 Some(Value::Json(serde_json::Value::Array(actions))) => actions.push(action),
421 Some(existing) => {
422 let previous = std::mem::replace(existing, Value::Null);
423 *existing = Value::Json(serde_json::Value::Array(vec![
424 previous.to_json_value(),
425 action,
426 ]));
427 }
428 None => {
429 dynamic.insert(
430 ACTION_LIST_KEY.to_owned(),
431 Value::Json(serde_json::Value::Array(vec![action])),
432 );
433 }
434 }
435}
436
437fn append_record_action(record: &mut Record, action: serde_json::Value) {
438 match record.get_mut(ACTION_LIST_KEY) {
439 Some(Value::Json(serde_json::Value::Array(actions))) => actions.push(action),
440 Some(existing) => {
441 let previous = std::mem::replace(existing, Value::Null);
442 *existing = Value::Json(serde_json::Value::Array(vec![
443 previous.to_json_value(),
444 action,
445 ]));
446 }
447 None => {
448 record.insert(
449 ACTION_LIST_KEY.to_owned(),
450 Value::Json(serde_json::Value::Array(vec![action])),
451 );
452 }
453 }
454}