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}