1use crate::error::{Result, SzConfigError};
7use crate::helpers::{
8 find_in_config_array, get_next_id, lookup_efunc_id, lookup_element_id, lookup_feature_id,
9};
10use serde_json::{Value, json};
11
12#[derive(Debug, Clone)]
18pub struct AddExpressionCallParams<'a> {
19 pub efunc_code: &'a str,
20 pub element_list: Vec<(String, String, Option<String>)>, pub ftype_code: Option<&'a str>,
22 pub felem_code: Option<&'a str>,
23 pub exec_order: Option<i64>,
24 pub expression_feature: Option<&'a str>,
25 pub is_virtual: &'a str,
26}
27
28impl<'a> AddExpressionCallParams<'a> {
29 pub fn new(efunc_code: &'a str, element_list: Vec<(String, String, Option<String>)>) -> Self {
30 Self {
31 efunc_code,
32 element_list,
33 ftype_code: None,
34 felem_code: None,
35 exec_order: None,
36 expression_feature: None,
37 is_virtual: "No",
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
44pub struct ExpressionCallElementParams {
45 pub ftype_id: i64,
46 pub felem_id: i64,
47 pub exec_order: i64,
48 pub felem_req: String,
49}
50
51impl ExpressionCallElementParams {
52 pub fn new(ftype_id: i64, felem_id: i64, exec_order: i64, felem_req: String) -> Self {
53 Self {
54 ftype_id,
55 felem_id,
56 exec_order,
57 felem_req,
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
64pub struct ExpressionCallElementKey {
65 pub ftype_id: i64,
66 pub felem_id: i64,
67 pub exec_order: i64,
68}
69
70impl ExpressionCallElementKey {
71 pub fn new(ftype_id: i64, felem_id: i64, exec_order: i64) -> Self {
72 Self {
73 ftype_id,
74 felem_id,
75 exec_order,
76 }
77 }
78}
79
80#[derive(Debug, Clone, Default)]
82pub struct SetExpressionCallParams {
83 pub efcall_id: i64,
84 pub exec_order: Option<i64>,
85}
86
87impl TryFrom<&Value> for SetExpressionCallParams {
88 type Error = SzConfigError;
89
90 fn try_from(json: &Value) -> Result<Self> {
91 let efcall_id = json
92 .get("efcallId")
93 .and_then(|v| v.as_i64())
94 .ok_or_else(|| SzConfigError::MissingField("efcallId".to_string()))?;
95
96 Ok(Self {
97 efcall_id,
98 exec_order: json.get("execOrder").and_then(|v| v.as_i64()),
99 })
100 }
101}
102
103pub fn add_expression_call(
120 config: &str,
121 params: AddExpressionCallParams,
122) -> Result<(String, Value)> {
123 let mut config_data: Value =
124 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
125
126 let efcall_id = get_next_id(&config_data, "G2_CONFIG.CFG_EFCALL", "EFCALL_ID", 1000)?;
128
129 let efunc_id = lookup_efunc_id(config, params.efunc_code)?;
131
132 let mut ftype_id: i64 = -1;
134 let mut felem_id: i64 = -1;
135
136 if let Some(feature) = params.ftype_code.filter(|f| !f.eq_ignore_ascii_case("ALL")) {
137 ftype_id = lookup_feature_id(config, feature)?;
138 }
139
140 if let Some(element) = params.felem_code.filter(|e| !e.eq_ignore_ascii_case("N/A")) {
141 felem_id = lookup_element_id(config, element)?;
142 }
143
144 if (ftype_id > 0 && felem_id > 0) || (ftype_id < 0 && felem_id < 0) {
146 return Err(SzConfigError::InvalidInput(
147 "Either a feature or an element must be specified, but not both".to_string(),
148 ));
149 }
150
151 let final_exec_order = if let Some(order) = params.exec_order {
153 let order_taken = config_data["G2_CONFIG"]["CFG_EFCALL"]
155 .as_array()
156 .map(|arr| {
157 arr.iter().any(|call| {
158 call["FTYPE_ID"].as_i64() == Some(ftype_id)
159 && call["FELEM_ID"].as_i64() == Some(felem_id)
160 && call["EXEC_ORDER"].as_i64() == Some(order)
161 })
162 })
163 .unwrap_or(false);
164
165 if order_taken {
166 return Err(SzConfigError::AlreadyExists(format!(
167 "Execution order {order} already taken for this feature/element"
168 )));
169 }
170 order
171 } else {
172 config_data["G2_CONFIG"]["CFG_EFCALL"]
174 .as_array()
175 .map(|arr| {
176 arr.iter()
177 .filter(|call| {
178 call["FTYPE_ID"].as_i64() == Some(ftype_id)
179 && call["FELEM_ID"].as_i64() == Some(felem_id)
180 })
181 .filter_map(|call| call["EXEC_ORDER"].as_i64())
182 .max()
183 .map(|max| max + 1)
184 .unwrap_or(1)
185 })
186 .unwrap_or(1)
187 };
188
189 let efeat_ftype_id = if let Some(expr_feat) = params
191 .expression_feature
192 .filter(|f| !f.eq_ignore_ascii_case("N/A"))
193 {
194 lookup_feature_id(config, expr_feat)?
195 } else {
196 -1
197 };
198
199 let mut efbom_records = Vec::new();
201 let mut bom_exec_order = 0;
202
203 for (element_code, required, feature_opt) in params.element_list {
204 bom_exec_order += 1;
205
206 let _bom_feature_name_for_errors = feature_opt.clone();
208
209 let bom_ftype_id =
211 if let Some(bom_feature) = feature_opt.filter(|f| !f.eq_ignore_ascii_case("PARENT")) {
212 if bom_feature.eq_ignore_ascii_case("parent") {
213 0 } else {
215 lookup_feature_id(config, &bom_feature)?
216 }
217 } else {
218 -1
219 };
220
221 let bom_felem_id = lookup_element_id(config, &element_code)?;
223
224 efbom_records.push(json!({
226 "EFCALL_ID": efcall_id,
227 "FTYPE_ID": bom_ftype_id,
228 "FELEM_ID": bom_felem_id,
229 "EXEC_ORDER": bom_exec_order,
230 "FELEM_REQ": required
231 }));
232 }
233
234 let new_record = json!({
236 "EFCALL_ID": efcall_id,
237 "FTYPE_ID": ftype_id,
238 "FELEM_ID": felem_id,
239 "EFUNC_ID": efunc_id,
240 "EXEC_ORDER": final_exec_order,
241 "EFEAT_FTYPE_ID": efeat_ftype_id,
242 "IS_VIRTUAL": params.is_virtual
243 });
244
245 if let Some(efcall_array) = config_data["G2_CONFIG"]["CFG_EFCALL"].as_array_mut() {
247 efcall_array.push(new_record.clone());
248 } else {
249 return Err(SzConfigError::MissingSection("CFG_EFCALL".to_string()));
250 }
251
252 if let Some(efbom_array) = config_data["G2_CONFIG"]["CFG_EFBOM"].as_array_mut() {
253 efbom_array.extend(efbom_records);
254 } else {
255 return Err(SzConfigError::MissingSection("CFG_EFBOM".to_string()));
256 }
257
258 let modified_config =
259 serde_json::to_string(&config_data).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
260
261 Ok((modified_config, new_record))
262}
263
264pub fn delete_expression_call(config: &str, efcall_id: i64) -> Result<String> {
278 let mut config_data: Value =
279 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
280
281 let call_exists = config_data["G2_CONFIG"]["CFG_EFCALL"]
283 .as_array()
284 .map(|arr| {
285 arr.iter()
286 .any(|call| call["EFCALL_ID"].as_i64() == Some(efcall_id))
287 })
288 .unwrap_or(false);
289
290 if !call_exists {
291 return Err(SzConfigError::NotFound(format!(
292 "Expression call ID {efcall_id} does not exist"
293 )));
294 }
295
296 if let Some(efcall_array) = config_data["G2_CONFIG"]["CFG_EFCALL"].as_array_mut() {
298 efcall_array.retain(|record| record["EFCALL_ID"].as_i64() != Some(efcall_id));
299 }
300
301 if let Some(efbom_array) = config_data["G2_CONFIG"]["CFG_EFBOM"].as_array_mut() {
303 efbom_array.retain(|record| record["EFCALL_ID"].as_i64() != Some(efcall_id));
304 }
305
306 serde_json::to_string(&config_data).map_err(|e| SzConfigError::JsonParse(e.to_string()))
307}
308
309pub fn get_expression_call(config: &str, efcall_id: i64) -> Result<Value> {
321 find_in_config_array(config, "CFG_EFCALL", "EFCALL_ID", &efcall_id.to_string())?.ok_or_else(
322 || SzConfigError::NotFound(format!("Expression call ID {efcall_id} does not exist")),
323 )
324}
325
326pub fn list_expression_calls(config: &str) -> Result<Vec<Value>> {
336 let config_data: Value =
337 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
338
339 let empty_array = vec![];
340 let efcall_array = config_data
341 .get("G2_CONFIG")
342 .and_then(|g| g.get("CFG_EFCALL"))
343 .and_then(|v| v.as_array())
344 .unwrap_or(&empty_array);
345
346 let ftype_array = config_data
347 .get("G2_CONFIG")
348 .and_then(|g| g.get("CFG_FTYPE"))
349 .and_then(|v| v.as_array())
350 .unwrap_or(&empty_array);
351
352 let felem_array = config_data
353 .get("G2_CONFIG")
354 .and_then(|g| g.get("CFG_FELEM"))
355 .and_then(|v| v.as_array())
356 .unwrap_or(&empty_array);
357
358 let efunc_array = config_data
359 .get("G2_CONFIG")
360 .and_then(|g| g.get("CFG_EFUNC"))
361 .and_then(|v| v.as_array())
362 .unwrap_or(&empty_array);
363
364 let resolve_ftype = |ftype_id: i64| -> String {
366 if ftype_id <= 0 {
367 "all".to_string()
368 } else {
369 ftype_array
370 .iter()
371 .find(|ft| ft.get("FTYPE_ID").and_then(|v| v.as_i64()) == Some(ftype_id))
372 .and_then(|ft| ft.get("FTYPE_CODE"))
373 .and_then(|v| v.as_str())
374 .unwrap_or("all")
375 .to_string()
376 }
377 };
378
379 let resolve_felem = |felem_id: i64| -> String {
380 if felem_id <= 0 {
381 "n/a".to_string()
382 } else {
383 felem_array
384 .iter()
385 .find(|fe| fe.get("FELEM_ID").and_then(|v| v.as_i64()) == Some(felem_id))
386 .and_then(|fe| fe.get("FELEM_CODE"))
387 .and_then(|v| v.as_str())
388 .unwrap_or("n/a")
389 .to_string()
390 }
391 };
392
393 let resolve_efunc = |efunc_id: i64| -> String {
394 efunc_array
395 .iter()
396 .find(|ef| ef.get("EFUNC_ID").and_then(|v| v.as_i64()) == Some(efunc_id))
397 .and_then(|ef| ef.get("EFUNC_CODE"))
398 .and_then(|v| v.as_str())
399 .unwrap_or("unknown")
400 .to_string()
401 };
402
403 let items: Vec<Value> = efcall_array
405 .iter()
406 .map(|item| {
407 let ftype_id = item.get("FTYPE_ID").and_then(|v| v.as_i64()).unwrap_or(0);
408 let felem_id = item.get("FELEM_ID").and_then(|v| v.as_i64()).unwrap_or(0);
409 let efunc_id = item.get("EFUNC_ID").and_then(|v| v.as_i64()).unwrap_or(0);
410
411 let efeat_ftype_id = item.get("EFEAT_FTYPE_ID").and_then(|v| v.as_i64()).unwrap_or(-1);
412
413 json!({
414 "id": item.get("EFCALL_ID").and_then(|v| v.as_i64()).unwrap_or(0),
415 "feature": resolve_ftype(ftype_id),
416 "element": resolve_felem(felem_id),
417 "execOrder": item.get("EXEC_ORDER").and_then(|v| v.as_i64()).unwrap_or(0),
418 "function": resolve_efunc(efunc_id),
419 "isVirtual": item.get("IS_VIRTUAL").and_then(|v| v.as_str()).unwrap_or("No"),
420 "expressionFeature": if efeat_ftype_id <= 0 { "n/a".to_string() } else { resolve_ftype(efeat_ftype_id) }
421 })
422 })
423 .collect();
424
425 Ok(items)
426}
427
428pub fn set_expression_call(config: &str, _params: SetExpressionCallParams) -> Result<String> {
437 Ok(config.to_string())
439}
440
441pub fn add_expression_call_element(
453 config: &str,
454 efcall_id: i64,
455 params: ExpressionCallElementParams,
456) -> Result<(String, Value)> {
457 let mut config_data: Value =
458 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
459
460 if params.ftype_id < 0 {
462 return Err(SzConfigError::InvalidInput(format!(
463 "{} is not a valid feature ID",
464 params.ftype_id
465 )));
466 }
467
468 if let Some(ebom_array) = config_data
472 .get("G2_CONFIG")
473 .and_then(|g| g.get("CFG_EFBOM"))
474 .and_then(|v| v.as_array())
475 {
476 for item in ebom_array {
477 if item.get("EFCALL_ID").and_then(|v| v.as_i64()) == Some(efcall_id)
478 && item.get("FTYPE_ID").and_then(|v| v.as_i64()) == Some(params.ftype_id)
479 && item.get("FELEM_ID").and_then(|v| v.as_i64()) == Some(params.felem_id)
480 {
484 return Err(SzConfigError::AlreadyExists(
485 "Feature/element already exists for call".to_string(),
486 ));
487 }
488 }
489 }
490
491 let new_record = json!({
493 "EFCALL_ID": efcall_id,
494 "FTYPE_ID": params.ftype_id,
495 "FELEM_ID": params.felem_id,
496 "EXEC_ORDER": params.exec_order,
497 "FELEM_REQ": params.felem_req
498 });
499
500 if let Some(ebom_array) = config_data["G2_CONFIG"]["CFG_EFBOM"].as_array_mut() {
502 ebom_array.push(new_record.clone());
503 } else {
504 return Err(SzConfigError::MissingSection("CFG_EFBOM".to_string()));
505 }
506
507 let modified_config =
508 serde_json::to_string(&config_data).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
509
510 Ok((modified_config, new_record))
511}
512
513pub fn delete_expression_call_element(
523 config: &str,
524 efcall_id: i64,
525 key: ExpressionCallElementKey,
526) -> Result<String> {
527 let mut config_data: Value =
528 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
529
530 let element_exists = config_data["G2_CONFIG"]["CFG_EFBOM"]
532 .as_array()
533 .map(|arr| {
534 arr.iter().any(|item| {
535 item.get("EFCALL_ID").and_then(|v| v.as_i64()) == Some(efcall_id)
536 && item.get("FTYPE_ID").and_then(|v| v.as_i64()) == Some(key.ftype_id)
537 && item.get("FELEM_ID").and_then(|v| v.as_i64()) == Some(key.felem_id)
538 && item.get("EXEC_ORDER").and_then(|v| v.as_i64()) == Some(key.exec_order)
539 })
540 })
541 .unwrap_or(false);
542
543 if !element_exists {
544 return Err(SzConfigError::NotFound(
545 "Expression call element not found".to_string(),
546 ));
547 }
548
549 if let Some(ebom_array) = config_data["G2_CONFIG"]["CFG_EFBOM"].as_array_mut() {
551 ebom_array.retain(|item| {
552 !(item.get("EFCALL_ID").and_then(|v| v.as_i64()) == Some(efcall_id)
553 && item.get("FTYPE_ID").and_then(|v| v.as_i64()) == Some(key.ftype_id)
554 && item.get("FELEM_ID").and_then(|v| v.as_i64()) == Some(key.felem_id)
555 && item.get("EXEC_ORDER").and_then(|v| v.as_i64()) == Some(key.exec_order))
556 });
557 }
558
559 serde_json::to_string(&config_data).map_err(|e| SzConfigError::JsonParse(e.to_string()))
560}
561
562pub fn set_expression_call_element(
571 config: &str,
572 _params: ExpressionCallElementParams,
573) -> Result<String> {
574 Ok(config.to_string())
576}