sz_configtool_lib/
helpers.rs1use crate::error::{Result, SzConfigError};
2use serde_json::Value;
3
4pub fn get_next_id_from_array(array: &[Value], id_field: &str) -> Result<i64> {
15 let max_id = array
16 .iter()
17 .filter_map(|item| item.get(id_field))
18 .filter_map(|v| v.as_i64())
19 .max()
20 .unwrap_or(0);
21
22 Ok(max_id + 1)
23}
24
25pub fn get_next_id(
42 config_data: &Value,
43 section_path: &str,
44 id_field: &str,
45 seed_value: i64,
46) -> Result<i64> {
47 let parts: Vec<&str> = section_path.split('.').collect();
49
50 let mut current = config_data;
51 for part in &parts {
52 current = current.get(part).ok_or_else(|| {
53 SzConfigError::MissingSection(format!("Section path '{section_path}' not found"))
54 })?;
55 }
56
57 let max_id = if let Some(items) = current.as_array() {
59 items
60 .iter()
61 .filter_map(|item| item.get(id_field).and_then(|v| v.as_i64()))
62 .max()
63 .unwrap_or(seed_value - 1)
64 } else {
65 seed_value - 1
66 };
67
68 Ok(std::cmp::max(max_id + 1, seed_value))
69}
70
71pub fn get_next_id_with_min(array: &[Value], id_field: &str, min_value: i64) -> Result<i64> {
84 let max_id = array
85 .iter()
86 .filter_map(|item| item.get(id_field))
87 .filter_map(|v| v.as_i64())
88 .max()
89 .unwrap_or(min_value - 1);
90
91 Ok(std::cmp::max(max_id + 1, min_value))
92}
93
94pub fn is_id_taken(array: &[Value], id_field: &str, id_value: i64) -> bool {
104 array
105 .iter()
106 .any(|item| item.get(id_field).and_then(|v| v.as_i64()) == Some(id_value))
107}
108
109pub fn get_desired_or_next_id(
128 array: &[Value],
129 id_field: &str,
130 desired_id: Option<i64>,
131 min_value: i64,
132) -> Result<i64> {
133 if let Some(id) = desired_id {
134 if id > 0 {
135 if is_id_taken(array, id_field, id) {
136 return Err(SzConfigError::AlreadyExists(format!(
137 "The specified ID {id} is already taken"
138 )));
139 }
140 return Ok(id);
141 }
142 }
143
144 get_next_id_with_min(array, id_field, min_value)
146}
147
148pub fn get_desired_or_next_id_from_section(
165 config_data: &Value,
166 section_path: &str,
167 id_field: &str,
168 desired_id: Option<i64>,
169 seed_value: i64,
170) -> Result<i64> {
171 let parts: Vec<&str> = section_path.split('.').collect();
173
174 let mut current = config_data;
175 for part in &parts {
176 current = current.get(part).ok_or_else(|| {
177 SzConfigError::MissingSection(format!("Section path '{section_path}' not found"))
178 })?;
179 }
180
181 let array = current.as_array().ok_or_else(|| {
182 SzConfigError::MissingSection(format!("Section '{section_path}' is not an array"))
183 })?;
184
185 get_desired_or_next_id(array, id_field, desired_id, seed_value)
186}
187
188pub fn find_in_array<'a>(array: &'a [Value], field: &str, value: &str) -> Option<&'a Value> {
198 array.iter().find(|item| {
199 item.get(field)
200 .and_then(|v| v.as_str())
201 .map(|s| s == value)
202 .unwrap_or(false)
203 })
204}
205
206pub fn find_in_array_mut<'a>(
216 array: &'a mut [Value],
217 field: &str,
218 value: &str,
219) -> Option<&'a mut Value> {
220 array.iter_mut().find(|item| {
221 item.get(field)
222 .and_then(|v| v.as_str())
223 .map(|s| s == value)
224 .unwrap_or(false)
225 })
226}
227
228pub fn add_to_config_array(config_json: &str, section: &str, item: Value) -> Result<String> {
242 let mut config: Value =
243 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
244
245 let array = config
246 .get_mut("G2_CONFIG")
247 .and_then(|g| g.get_mut(section))
248 .and_then(|v| v.as_array_mut())
249 .ok_or_else(|| SzConfigError::MissingSection(section.to_string()))?;
250
251 array.push(item);
252
253 serde_json::to_string(&config).map_err(|e| SzConfigError::JsonParse(e.to_string()))
254}
255
256pub fn delete_from_config_array(
272 config_json: &str,
273 section: &str,
274 field: &str,
275 value: &str,
276) -> Result<String> {
277 let mut config: Value =
278 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
279
280 let array = config
281 .get_mut("G2_CONFIG")
282 .and_then(|g| g.get_mut(section))
283 .and_then(|v| v.as_array_mut())
284 .ok_or_else(|| SzConfigError::MissingSection(section.to_string()))?;
285
286 let original_len = array.len();
287 array.retain(|item| {
288 item.get(field)
289 .and_then(|v| v.as_str())
290 .map(|s| s != value)
291 .unwrap_or(true)
292 });
293
294 if array.len() == original_len {
295 return Err(SzConfigError::NotFound(format!(
296 "{section} '{value}' not found"
297 )));
298 }
299
300 serde_json::to_string(&config).map_err(|e| SzConfigError::JsonParse(e.to_string()))
301}
302
303pub fn find_in_config_array(
317 config_json: &str,
318 section: &str,
319 field: &str,
320 value: &str,
321) -> Result<Option<Value>> {
322 let config: Value =
323 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
324
325 let array = config
326 .get("G2_CONFIG")
327 .and_then(|g| g.get(section))
328 .and_then(|v| v.as_array());
329
330 if let Some(arr) = array {
331 let item = arr.iter().find(|item| {
332 item.get(field)
333 .and_then(|v| v.as_str())
334 .map(|s| s == value)
335 .or_else(|| {
336 item.get(field)
338 .and_then(|v| v.as_i64())
339 .and_then(|id| value.parse::<i64>().ok().map(|val| id == val))
340 })
341 .unwrap_or(false)
342 });
343 Ok(item.cloned())
344 } else {
345 Ok(None)
346 }
347}
348
349pub fn remove_from_config_array(
351 config_json: &str,
352 section: &str,
353 field: &str,
354 value: &str,
355) -> Result<String> {
356 delete_from_config_array(config_json, section, field, value)
357}
358
359pub fn update_in_config_array(
371 config_json: &str,
372 section: &str,
373 field: &str,
374 value: &str,
375 new_item: Value,
376) -> Result<String> {
377 let mut config: Value =
378 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
379
380 let array = config
381 .get_mut("G2_CONFIG")
382 .and_then(|g| g.get_mut(section))
383 .and_then(|v| v.as_array_mut())
384 .ok_or_else(|| SzConfigError::MissingSection(section.to_string()))?;
385
386 let item = find_in_array_mut(array, field, value)
387 .ok_or_else(|| SzConfigError::NotFound(format!("{section} '{value}' not found")))?;
388
389 *item = new_item;
391
392 serde_json::to_string(&config).map_err(|e| SzConfigError::JsonParse(e.to_string()))
393}
394
395pub fn list_from_config_array(config_json: &str, section: &str) -> Result<Vec<Value>> {
407 let config: Value =
408 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
409
410 let items = if let Some(g2_config) = config.get("G2_CONFIG") {
411 if let Some(array) = g2_config.get(section).and_then(|v| v.as_array()) {
412 array.clone()
413 } else {
414 Vec::new()
415 }
416 } else {
417 Vec::new()
418 };
419
420 Ok(items)
421}
422
423pub fn lookup_feature_id(config_json: &str, feature_code: &str) -> Result<i64> {
435 let config: Value =
436 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
437
438 config
439 .get("G2_CONFIG")
440 .and_then(|g| g.get("CFG_FTYPE"))
441 .and_then(|v| v.as_array())
442 .and_then(|arr| {
443 arr.iter()
444 .find(|f| {
445 f.get("FTYPE_CODE")
446 .and_then(|v| v.as_str())
447 .map(|s| s.eq_ignore_ascii_case(feature_code))
448 .unwrap_or(false)
449 })
450 .and_then(|f| f.get("FTYPE_ID"))
451 .and_then(|v| v.as_i64())
452 })
453 .ok_or_else(|| SzConfigError::NotFound(format!("Feature '{feature_code}' not found")))
454}
455
456pub fn lookup_element_id(config_json: &str, element_code: &str) -> Result<i64> {
468 let config: Value =
469 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
470
471 config
472 .get("G2_CONFIG")
473 .and_then(|g| g.get("CFG_FELEM"))
474 .and_then(|v| v.as_array())
475 .and_then(|arr| {
476 arr.iter()
477 .find(|e| {
478 e.get("FELEM_CODE")
479 .and_then(|v| v.as_str())
480 .map(|s| s.eq_ignore_ascii_case(element_code))
481 .unwrap_or(false)
482 })
483 .and_then(|e| e.get("FELEM_ID"))
484 .and_then(|v| v.as_i64())
485 })
486 .ok_or_else(|| SzConfigError::NotFound(format!("Element '{element_code}' not found")))
487}
488
489pub fn lookup_sfunc_id(config_json: &str, func_code: &str) -> Result<i64> {
501 let config: Value =
502 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
503
504 config
505 .get("G2_CONFIG")
506 .and_then(|g| g.get("CFG_SFUNC"))
507 .and_then(|v| v.as_array())
508 .and_then(|arr| {
509 arr.iter()
510 .find(|f| {
511 f.get("SFUNC_CODE")
512 .and_then(|v| v.as_str())
513 .map(|s| s.eq_ignore_ascii_case(func_code))
514 .unwrap_or(false)
515 })
516 .and_then(|f| f.get("SFUNC_ID"))
517 .and_then(|v| v.as_i64())
518 })
519 .ok_or_else(|| {
520 SzConfigError::NotFound(format!("Standardize function '{func_code}' not found"))
521 })
522}
523
524pub fn lookup_efunc_id(config_json: &str, func_code: &str) -> Result<i64> {
536 let config: Value =
537 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
538
539 config
540 .get("G2_CONFIG")
541 .and_then(|g| g.get("CFG_EFUNC"))
542 .and_then(|v| v.as_array())
543 .and_then(|arr| {
544 arr.iter()
545 .find(|f| {
546 f.get("EFUNC_CODE")
547 .and_then(|v| v.as_str())
548 .map(|s| s.eq_ignore_ascii_case(func_code))
549 .unwrap_or(false)
550 })
551 .and_then(|f| f.get("EFUNC_ID"))
552 .and_then(|v| v.as_i64())
553 })
554 .ok_or_else(|| {
555 SzConfigError::NotFound(format!("Expression function '{func_code}' not found"))
556 })
557}
558
559pub fn lookup_cfunc_id(config_json: &str, func_code: &str) -> Result<i64> {
571 let config: Value =
572 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
573
574 config
575 .get("G2_CONFIG")
576 .and_then(|g| g.get("CFG_CFUNC"))
577 .and_then(|v| v.as_array())
578 .and_then(|arr| {
579 arr.iter()
580 .find(|f| {
581 f.get("CFUNC_CODE")
582 .and_then(|v| v.as_str())
583 .map(|s| s.eq_ignore_ascii_case(func_code))
584 .unwrap_or(false)
585 })
586 .and_then(|f| f.get("CFUNC_ID"))
587 .and_then(|v| v.as_i64())
588 })
589 .ok_or_else(|| {
590 SzConfigError::NotFound(format!("Comparison function '{func_code}' not found"))
591 })
592}
593
594pub fn lookup_dfunc_id(config_json: &str, func_code: &str) -> Result<i64> {
606 let config: Value =
607 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
608
609 config
610 .get("G2_CONFIG")
611 .and_then(|g| g.get("CFG_DFUNC"))
612 .and_then(|v| v.as_array())
613 .and_then(|arr| {
614 arr.iter()
615 .find(|f| {
616 f.get("DFUNC_CODE")
617 .and_then(|v| v.as_str())
618 .map(|s| s.eq_ignore_ascii_case(func_code))
619 .unwrap_or(false)
620 })
621 .and_then(|f| f.get("DFUNC_ID"))
622 .and_then(|v| v.as_i64())
623 })
624 .ok_or_else(|| {
625 SzConfigError::NotFound(format!("Distinct function '{func_code}' not found"))
626 })
627}
628
629pub fn lookup_gplan_id(config_json: &str, plan_code: &str) -> Result<i64> {
641 let config: Value =
642 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
643
644 config
645 .get("G2_CONFIG")
646 .and_then(|g| g.get("CFG_GPLAN"))
647 .and_then(|v| v.as_array())
648 .and_then(|arr| {
649 arr.iter()
650 .find(|p| {
651 p.get("GPLAN_CODE")
652 .and_then(|v| v.as_str())
653 .map(|s| s.eq_ignore_ascii_case(plan_code))
654 .unwrap_or(false)
655 })
656 .and_then(|p| p.get("GPLAN_ID"))
657 .and_then(|v| v.as_i64())
658 })
659 .ok_or_else(|| SzConfigError::NotFound(format!("Generic plan '{plan_code}' not found")))
660}
661
662pub(crate) fn lookup_gplan_code(config_json: &str, gplan_id: i64) -> Result<String> {
674 let config: Value =
675 serde_json::from_str(config_json).map_err(|e| SzConfigError::JsonParse(e.to_string()))?;
676
677 config
678 .get("G2_CONFIG")
679 .and_then(|g| g.get("CFG_GPLAN"))
680 .and_then(|v| v.as_array())
681 .and_then(|arr| {
682 arr.iter()
683 .find(|p| p.get("GPLAN_ID").and_then(|v| v.as_i64()) == Some(gplan_id))
684 .and_then(|p| p.get("GPLAN_CODE"))
685 .and_then(|v| v.as_str())
686 .map(|s| s.to_string())
687 })
688 .ok_or_else(|| SzConfigError::NotFound(format!("Generic plan ID: {gplan_id}")))
689}