sz_configtool_lib/calls/
comparison.rs1use crate::error::{Result, SzConfigError};
7use crate::helpers::{
8 find_in_config_array, get_next_id, lookup_cfunc_id, lookup_element_id, lookup_feature_id,
9};
10use serde_json::{Value, json};
11
12#[derive(Debug, Clone)]
18pub struct AddComparisonCallParams {
19 pub ftype_code: String,
20 pub cfunc_code: String,
21 pub element_list: Vec<String>,
22}
23
24impl TryFrom<&Value> for AddComparisonCallParams {
25 type Error = SzConfigError;
26
27 fn try_from(json: &Value) -> Result<Self> {
28 Ok(Self {
29 ftype_code: json
30 .get("ftypeCode")
31 .and_then(|v| v.as_str())
32 .ok_or_else(|| SzConfigError::MissingField("ftypeCode".to_string()))?
33 .to_string(),
34 cfunc_code: json
35 .get("cfuncCode")
36 .and_then(|v| v.as_str())
37 .ok_or_else(|| SzConfigError::MissingField("cfuncCode".to_string()))?
38 .to_string(),
39 element_list: json
40 .get("elementList")
41 .and_then(|v| v.as_array())
42 .map(|arr| {
43 arr.iter()
44 .filter_map(|v| v.as_str().map(|s| s.to_string()))
45 .collect()
46 })
47 .unwrap_or_default(),
48 })
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct AddComparisonCallElementParams {
55 pub cfcall_id: i64,
56 pub ftype_id: i64,
57 pub felem_id: i64,
58 pub exec_order: i64,
59}
60
61impl TryFrom<&Value> for AddComparisonCallElementParams {
62 type Error = SzConfigError;
63
64 fn try_from(json: &Value) -> Result<Self> {
65 Ok(Self {
66 cfcall_id: json
67 .get("cfcallId")
68 .and_then(|v| v.as_i64())
69 .ok_or_else(|| SzConfigError::MissingField("cfcallId".to_string()))?,
70 ftype_id: json
71 .get("ftypeId")
72 .and_then(|v| v.as_i64())
73 .ok_or_else(|| SzConfigError::MissingField("ftypeId".to_string()))?,
74 felem_id: json
75 .get("felemId")
76 .and_then(|v| v.as_i64())
77 .ok_or_else(|| SzConfigError::MissingField("felemId".to_string()))?,
78 exec_order: json
79 .get("execOrder")
80 .and_then(|v| v.as_i64())
81 .ok_or_else(|| SzConfigError::MissingField("execOrder".to_string()))?,
82 })
83 }
84}
85
86#[derive(Debug, Clone)]
88pub struct DeleteComparisonCallElementParams {
89 pub ftype_id: i64,
90 pub felem_id: i64,
91 pub exec_order: i64,
92}
93
94#[derive(Debug, Clone, Default)]
96pub struct SetComparisonCallParams {
97 pub cfcall_id: i64,
98 pub exec_order: Option<i64>,
99}
100
101impl TryFrom<&Value> for SetComparisonCallParams {
102 type Error = SzConfigError;
103
104 fn try_from(json: &Value) -> Result<Self> {
105 let cfcall_id = json
106 .get("cfcallId")
107 .and_then(|v| v.as_i64())
108 .ok_or_else(|| SzConfigError::MissingField("cfcallId".to_string()))?;
109
110 Ok(Self {
111 cfcall_id,
112 exec_order: json.get("execOrder").and_then(|v| v.as_i64()),
113 })
114 }
115}
116
117#[derive(Debug, Clone)]
119pub struct SetComparisonCallElementParams {
120 pub ftype_id: i64,
121 pub felem_id: i64,
122 pub exec_order: i64,
123 pub updates: Value,
124}
125
126pub fn add_comparison_call(
143 config: &str,
144 params: AddComparisonCallParams,
145) -> Result<(String, Value)> {
146 let mut config_data: Value =
147 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
148
149 let cfcall_id = get_next_id(&config_data, "G2_CONFIG.CFG_CFCALL", "CFCALL_ID", 1000)?;
151
152 let ftype_id = lookup_feature_id(config, ¶ms.ftype_code)?;
154
155 let call_exists = config_data["G2_CONFIG"]["CFG_CFCALL"]
157 .as_array()
158 .map(|arr| {
159 arr.iter()
160 .any(|call| call["FTYPE_ID"].as_i64() == Some(ftype_id))
161 })
162 .unwrap_or(false);
163
164 if call_exists {
165 return Err(SzConfigError::AlreadyExists(format!(
166 "Comparison call for feature {} already set",
167 params.ftype_code
168 )));
169 }
170
171 let cfunc_id = lookup_cfunc_id(config, ¶ms.cfunc_code)?;
173
174 if params.element_list.is_empty() {
176 return Err(SzConfigError::InvalidInput(
177 "No elements were found in the elementList".to_string(),
178 ));
179 }
180
181 let mut cfbom_records = Vec::new();
183 let mut exec_order = 0;
184
185 for (idx, element_code) in params.element_list.iter().enumerate() {
186 exec_order += 1;
187
188 if element_code.trim().is_empty() {
190 return Err(SzConfigError::InvalidInput(format!(
191 "Element cannot be blank in item {} on the element list",
192 idx + 1
193 )));
194 }
195
196 let bom_felem_id = lookup_element_id(config, element_code)?;
198
199 cfbom_records.push(json!({
201 "CFCALL_ID": cfcall_id,
202 "FTYPE_ID": ftype_id,
203 "FELEM_ID": bom_felem_id,
204 "EXEC_ORDER": exec_order
205 }));
206 }
207
208 let new_record = json!({
210 "CFCALL_ID": cfcall_id,
211 "FTYPE_ID": ftype_id,
212 "CFUNC_ID": cfunc_id
213 });
214
215 if let Some(cfcall_array) = config_data["G2_CONFIG"]["CFG_CFCALL"].as_array_mut() {
217 cfcall_array.push(new_record.clone());
218 } else {
219 return Err(SzConfigError::MissingSection("CFG_CFCALL".to_string()));
220 }
221
222 if let Some(cfbom_array) = config_data["G2_CONFIG"]["CFG_CFBOM"].as_array_mut() {
223 cfbom_array.extend(cfbom_records);
224 } else {
225 return Err(SzConfigError::MissingSection("CFG_CFBOM".to_string()));
226 }
227
228 let modified_config =
229 serde_json::to_string(&config_data).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
230
231 Ok((modified_config, new_record))
232}
233
234pub fn delete_comparison_call(config: &str, cfcall_id: i64) -> Result<String> {
248 let mut config_data: Value =
249 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
250
251 let call_exists = config_data["G2_CONFIG"]["CFG_CFCALL"]
253 .as_array()
254 .map(|arr| {
255 arr.iter()
256 .any(|call| call["CFCALL_ID"].as_i64() == Some(cfcall_id))
257 })
258 .unwrap_or(false);
259
260 if !call_exists {
261 return Err(SzConfigError::NotFound(format!(
262 "Comparison call ID {cfcall_id} does not exist"
263 )));
264 }
265
266 if let Some(cfcall_array) = config_data["G2_CONFIG"]["CFG_CFCALL"].as_array_mut() {
268 cfcall_array.retain(|record| record["CFCALL_ID"].as_i64() != Some(cfcall_id));
269 }
270
271 if let Some(cfbom_array) = config_data["G2_CONFIG"]["CFG_CFBOM"].as_array_mut() {
273 cfbom_array.retain(|record| record["CFCALL_ID"].as_i64() != Some(cfcall_id));
274 }
275
276 serde_json::to_string(&config_data).map_err(|e| SzConfigError::JsonParse(e.to_string()))
277}
278
279pub fn get_comparison_call(config: &str, cfcall_id: i64) -> Result<Value> {
291 find_in_config_array(config, "CFG_CFCALL", "CFCALL_ID", &cfcall_id.to_string())?
292 .ok_or_else(|| SzConfigError::NotFound(format!("Comparison call ID {cfcall_id}")))
293}
294
295pub fn list_comparison_calls(config: &str) -> Result<Vec<Value>> {
305 let config_data: Value =
306 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
307
308 let empty_array = vec![];
309 let cfcall_array = config_data
310 .get("G2_CONFIG")
311 .and_then(|g| g.get("CFG_CFCALL"))
312 .and_then(|v| v.as_array())
313 .unwrap_or(&empty_array);
314
315 let ftype_array = config_data
316 .get("G2_CONFIG")
317 .and_then(|g| g.get("CFG_FTYPE"))
318 .and_then(|v| v.as_array())
319 .unwrap_or(&empty_array);
320
321 let cfunc_array = config_data
322 .get("G2_CONFIG")
323 .and_then(|g| g.get("CFG_CFUNC"))
324 .and_then(|v| v.as_array())
325 .unwrap_or(&empty_array);
326
327 let resolve_ftype = |ftype_id: i64| -> String {
329 ftype_array
330 .iter()
331 .find(|ft| ft.get("FTYPE_ID").and_then(|v| v.as_i64()) == Some(ftype_id))
332 .and_then(|ft| ft.get("FTYPE_CODE"))
333 .and_then(|v| v.as_str())
334 .unwrap_or("unknown")
335 .to_string()
336 };
337
338 let resolve_cfunc = |cfunc_id: i64| -> String {
339 cfunc_array
340 .iter()
341 .find(|cf| cf.get("CFUNC_ID").and_then(|v| v.as_i64()) == Some(cfunc_id))
342 .and_then(|cf| cf.get("CFUNC_CODE"))
343 .and_then(|v| v.as_str())
344 .unwrap_or("unknown")
345 .to_string()
346 };
347
348 let items: Vec<Value> = cfcall_array
350 .iter()
351 .map(|item| {
352 let ftype_id = item.get("FTYPE_ID").and_then(|v| v.as_i64()).unwrap_or(0);
353 let cfunc_id = item.get("CFUNC_ID").and_then(|v| v.as_i64()).unwrap_or(0);
354
355 json!({
356 "id": item.get("CFCALL_ID").and_then(|v| v.as_i64()).unwrap_or(0),
357 "feature": resolve_ftype(ftype_id),
358 "function": resolve_cfunc(cfunc_id)
359 })
360 })
361 .collect();
362
363 Ok(items)
364}
365
366pub fn set_comparison_call(config: &str, _params: SetComparisonCallParams) -> Result<String> {
375 Ok(config.to_string())
377}
378
379pub fn add_comparison_call_element(
390 config: &str,
391 params: AddComparisonCallElementParams,
392) -> Result<(String, Value)> {
393 let mut config_data: Value =
394 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
395
396 if params.ftype_id < 0 {
398 return Err(SzConfigError::InvalidInput(format!(
399 "{} is not a valid feature ID",
400 params.ftype_id
401 )));
402 }
403
404 if let Some(cbom_array) = config_data
408 .get("G2_CONFIG")
409 .and_then(|g| g.get("CFG_CFBOM"))
410 .and_then(|v| v.as_array())
411 {
412 for item in cbom_array {
413 if item.get("CFCALL_ID").and_then(|v| v.as_i64()) == Some(params.cfcall_id)
414 && item.get("FTYPE_ID").and_then(|v| v.as_i64()) == Some(params.ftype_id)
415 && item.get("FELEM_ID").and_then(|v| v.as_i64()) == Some(params.felem_id)
416 {
420 return Err(SzConfigError::AlreadyExists(
421 "Feature/element already exists for call".to_string(),
422 ));
423 }
424 }
425 }
426
427 let new_record = json!({
429 "CFCALL_ID": params.cfcall_id,
430 "FTYPE_ID": params.ftype_id,
431 "FELEM_ID": params.felem_id,
432 "EXEC_ORDER": params.exec_order
433 });
434
435 if let Some(cbom_array) = config_data["G2_CONFIG"]["CFG_CFBOM"].as_array_mut() {
437 cbom_array.push(new_record.clone());
438 } else {
439 return Err(SzConfigError::MissingSection("CFG_CFBOM".to_string()));
440 }
441
442 let modified_config =
443 serde_json::to_string(&config_data).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
444
445 Ok((modified_config, new_record))
446}
447
448pub fn delete_comparison_call_element(
458 config: &str,
459 cfcall_id: i64,
460 params: DeleteComparisonCallElementParams,
461) -> Result<String> {
462 let mut config_data: Value =
463 serde_json::from_str(config).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
464
465 let element_exists = config_data["G2_CONFIG"]["CFG_CFBOM"]
467 .as_array()
468 .map(|arr| {
469 arr.iter().any(|item| {
470 item.get("CFCALL_ID").and_then(|v| v.as_i64()) == Some(cfcall_id)
471 && item.get("FTYPE_ID").and_then(|v| v.as_i64()) == Some(params.ftype_id)
472 && item.get("FELEM_ID").and_then(|v| v.as_i64()) == Some(params.felem_id)
473 && item.get("EXEC_ORDER").and_then(|v| v.as_i64()) == Some(params.exec_order)
474 })
475 })
476 .unwrap_or(false);
477
478 if !element_exists {
479 return Err(SzConfigError::NotFound(
480 "Comparison call element not found".to_string(),
481 ));
482 }
483
484 if let Some(cbom_array) = config_data["G2_CONFIG"]["CFG_CFBOM"].as_array_mut() {
486 cbom_array.retain(|item| {
487 !(item.get("CFCALL_ID").and_then(|v| v.as_i64()) == Some(cfcall_id)
488 && item.get("FTYPE_ID").and_then(|v| v.as_i64()) == Some(params.ftype_id)
489 && item.get("FELEM_ID").and_then(|v| v.as_i64()) == Some(params.felem_id)
490 && item.get("EXEC_ORDER").and_then(|v| v.as_i64()) == Some(params.exec_order))
491 });
492 }
493
494 serde_json::to_string(&config_data).map_err(|e| SzConfigError::JsonParse(e.to_string()))
495}
496
497pub fn set_comparison_call_element(
507 config: &str,
508 _cfcall_id: i64,
509 _params: SetComparisonCallElementParams,
510) -> Result<String> {
511 Ok(config.to_string())
513}