sz_configtool_lib/
rules.rs1use crate::error::{Result, SzConfigError};
7use crate::helpers;
8use serde_json::{Value, json};
9
10#[derive(Debug, Clone)]
16pub struct SetRuleParams<'a> {
17 pub code: &'a str,
18 pub resolve: Option<&'a str>,
19 pub relate: Option<&'a str>,
20 pub rtype_id: Option<i64>,
21 pub fragment: Option<&'a str>,
22 pub disqualifier: Option<&'a str>,
23 pub tier: Option<i64>,
24}
25
26impl<'a> TryFrom<&'a Value> for SetRuleParams<'a> {
27 type Error = SzConfigError;
28
29 fn try_from(json: &'a Value) -> Result<Self> {
30 let code = json
31 .get("code")
32 .and_then(|v| v.as_str())
33 .or_else(|| json.get("rule").and_then(|v| v.as_str()))
34 .ok_or_else(|| SzConfigError::MissingField("code or rule".to_string()))?;
35
36 Ok(Self {
37 code,
38 resolve: json
39 .get("resolve")
40 .and_then(|v| v.as_str())
41 .or_else(|| json.get("RESOLVE").and_then(|v| v.as_str())),
42 relate: json
43 .get("relate")
44 .and_then(|v| v.as_str())
45 .or_else(|| json.get("RELATE").and_then(|v| v.as_str())),
46 rtype_id: json
47 .get("rtypeId")
48 .and_then(|v| v.as_i64())
49 .or_else(|| json.get("RTYPE_ID").and_then(|v| v.as_i64())),
50 fragment: json
51 .get("fragment")
52 .and_then(|v| v.as_str())
53 .or_else(|| json.get("FRAGMENT").and_then(|v| v.as_str())),
54 disqualifier: json
55 .get("disqualifier")
56 .and_then(|v| v.as_str())
57 .or_else(|| json.get("DISQUALIFIER").and_then(|v| v.as_str())),
58 tier: json
59 .get("tier")
60 .and_then(|v| v.as_i64())
61 .or_else(|| json.get("TIER").and_then(|v| v.as_i64())),
62 })
63 }
64}
65
66pub fn add_rule(config_json: &str, id: i64, rule_config: &Value) -> Result<(String, i64)> {
94 let code = rule_config
95 .get("ERRULE_CODE")
96 .and_then(|v| v.as_str())
97 .ok_or_else(|| SzConfigError::MissingField("ERRULE_CODE".to_string()))?;
98
99 let config_data: Value = serde_json::from_str(config_json)?;
101 if let Some(errule_array) = config_data
102 .get("G2_CONFIG")
103 .and_then(|g| g.get("CFG_ERRULE"))
104 .and_then(|v| v.as_array())
105 {
106 if errule_array
107 .iter()
108 .any(|item| item.get("ERRULE_ID").and_then(|v| v.as_i64()) == Some(id))
109 {
110 return Err(SzConfigError::AlreadyExists(
111 "The specified ID is already taken".to_string(),
112 ));
113 }
114 }
115
116 let mut new_item = rule_config.clone();
118 if let Some(obj) = new_item.as_object_mut() {
119 obj.insert("ERRULE_ID".to_string(), json!(id));
120 obj.insert("ERRULE_CODE".to_string(), json!(code.to_uppercase()));
121 }
122
123 let modified_json = helpers::add_to_config_array(config_json, "CFG_ERRULE", new_item)?;
125
126 Ok((modified_json, id))
127}
128
129pub fn delete_rule(config_json: &str, rule_code: &str) -> Result<String> {
149 let rule_code = rule_code.to_uppercase();
150
151 let _ = helpers::find_in_config_array(config_json, "CFG_ERRULE", "ERRULE_CODE", &rule_code)?
153 .ok_or_else(|| SzConfigError::NotFound(format!("Rule not found: {rule_code}")))?;
154
155 helpers::remove_from_config_array(config_json, "CFG_ERRULE", "ERRULE_CODE", &rule_code)
157}
158
159pub fn get_rule(config_json: &str, code_or_id: &str) -> Result<Value> {
179 let search_value = code_or_id.to_uppercase();
180
181 let item = if let Some(item) =
183 helpers::find_in_config_array(config_json, "CFG_ERRULE", "ERRULE_CODE", &search_value)?
184 {
185 item
186 } else if let Some(item) =
187 helpers::find_in_config_array(config_json, "CFG_ERRULE", "ERRULE_ID", &search_value)?
188 {
189 item
190 } else {
191 return Err(SzConfigError::NotFound(format!(
192 "Rule not found: {search_value}"
193 )));
194 };
195
196 let resolve = item.get("RESOLVE").and_then(|v| v.as_str()).unwrap_or("");
198 let tier = if resolve == "Yes" {
199 item.get("ERRULE_TIER").and_then(|v| v.as_i64())
200 } else {
201 None
202 };
203
204 Ok(json!({
205 "id": item.get("ERRULE_ID").and_then(|v| v.as_i64()).unwrap_or(0),
206 "rule": item.get("ERRULE_CODE").and_then(|v| v.as_str()).unwrap_or(""),
207 "resolve": resolve,
208 "relate": item.get("RELATE").and_then(|v| v.as_str()).unwrap_or(""),
209 "rtype_id": item.get("RTYPE_ID").and_then(|v| v.as_i64()).unwrap_or(0),
210 "fragment": item.get("QUAL_ERFRAG_CODE").and_then(|v| v.as_str()).unwrap_or(""),
211 "disqualifier": item.get("DISQ_ERFRAG_CODE").and_then(|v| v.as_str()).unwrap_or(""),
212 "tier": tier
213 }))
214}
215
216pub fn list_rules(config_json: &str) -> Result<Vec<Value>> {
236 let config_data: Value = serde_json::from_str(config_json)?;
237
238 let items: Vec<Value> = if let Some(g2_config) = config_data.get("G2_CONFIG") {
240 if let Some(array) = g2_config.get("CFG_ERRULE").and_then(|v| v.as_array()) {
241 array
242 .iter()
243 .map(|item| {
244 let resolve = item.get("RESOLVE").and_then(|v| v.as_str()).unwrap_or("");
245 let tier = if resolve == "Yes" {
246 item.get("ERRULE_TIER").and_then(|v| v.as_i64())
247 } else {
248 None
249 };
250
251 json!({
252 "id": item.get("ERRULE_ID").and_then(|v| v.as_i64()).unwrap_or(0),
253 "rule": item.get("ERRULE_CODE").and_then(|v| v.as_str()).unwrap_or(""),
254 "resolve": resolve,
255 "relate": item.get("RELATE").and_then(|v| v.as_str()).unwrap_or(""),
256 "rtype_id": item.get("RTYPE_ID").and_then(|v| v.as_i64()).unwrap_or(0),
257 "fragment": item.get("QUAL_ERFRAG_CODE").and_then(|v| v.as_str()).unwrap_or(""),
258 "disqualifier": item.get("DISQ_ERFRAG_CODE").and_then(|v| v.as_str()).unwrap_or(""),
259 "tier": tier
260 })
261 })
262 .collect()
263 } else {
264 Vec::new()
265 }
266 } else {
267 Vec::new()
268 };
269
270 Ok(items)
271}
272
273pub fn set_rule(config_json: &str, params: SetRuleParams) -> Result<String> {
303 let code = params.code.to_uppercase();
304
305 let existing_rule =
307 helpers::find_in_config_array(config_json, "CFG_ERRULE", "ERRULE_CODE", &code)?
308 .ok_or_else(|| SzConfigError::NotFound(format!("Rule not found: {code}")))?;
309
310 if let Some(frag) = params.fragment {
312 let frag_upper = frag.to_uppercase();
313 helpers::find_in_config_array(config_json, "CFG_ERFRAG", "ERFRAG_CODE", &frag_upper)?
314 .ok_or_else(|| SzConfigError::NotFound(format!("Fragment '{frag_upper}' not found")))?;
315 }
316
317 if let Some(disq) = params.disqualifier {
319 let disq_upper = disq.to_uppercase();
320 helpers::find_in_config_array(config_json, "CFG_ERFRAG", "ERFRAG_CODE", &disq_upper)?
321 .ok_or_else(|| SzConfigError::NotFound(format!("Fragment '{disq_upper}' not found")))?;
322 }
323
324 let resolve_value = params
326 .resolve
327 .or_else(|| existing_rule.get("RESOLVE").and_then(|v| v.as_str()))
328 .unwrap_or("No");
329
330 let resolve_upper = resolve_value.to_uppercase();
332 if resolve_upper != "YES" && resolve_upper != "NO" {
333 return Err(SzConfigError::InvalidInput(
334 "resolve value must be in [\"Yes\", \"No\"]".to_string(),
335 ));
336 }
337 let final_resolve = if resolve_upper == "YES" { "Yes" } else { "No" };
338
339 let relate_value = params
341 .relate
342 .or_else(|| existing_rule.get("RELATE").and_then(|v| v.as_str()))
343 .unwrap_or("No");
344
345 let relate_upper = relate_value.to_uppercase();
347 if relate_upper != "YES" && relate_upper != "NO" {
348 return Err(SzConfigError::InvalidInput(
349 "relate value must be in [\"Yes\", \"No\"]".to_string(),
350 ));
351 }
352 let final_relate = if relate_upper == "YES" { "Yes" } else { "No" };
353
354 if final_resolve == "Yes" && final_relate == "Yes" {
356 return Err(SzConfigError::InvalidInput(
357 "A rule must either resolve or relate, please set the other to No".to_string(),
358 ));
359 }
360
361 let mut final_rtype_id = params
363 .rtype_id
364 .or_else(|| existing_rule.get("RTYPE_ID").and_then(|v| v.as_i64()))
365 .unwrap_or(1);
366
367 if final_resolve == "Yes" && final_rtype_id != 1 {
370 final_rtype_id = 1;
371 }
372
373 if final_relate == "Yes" && ![2, 3, 4].contains(&final_rtype_id) {
375 return Err(SzConfigError::InvalidInput(
376 "Relationship type (RTYPE_ID) must be set to either 2=Possible match or 3=Possibly related".to_string(),
377 ));
378 }
379
380 let errule_id = existing_rule
382 .get("ERRULE_ID")
383 .and_then(|v| v.as_i64())
384 .unwrap_or(0);
385
386 let mut updated_item = json!({
388 "ERRULE_ID": errule_id,
389 "ERRULE_CODE": code.clone()
390 });
391
392 if let Some(obj) = updated_item.as_object_mut() {
393 if params.resolve.is_some() {
394 obj.insert("RESOLVE".to_string(), json!(final_resolve));
395 }
396 if params.relate.is_some() {
397 obj.insert("RELATE".to_string(), json!(final_relate));
398 }
399 if params.rtype_id.is_some() || params.resolve.is_some() {
401 obj.insert("RTYPE_ID".to_string(), json!(final_rtype_id));
402 }
403 if let Some(frag) = params.fragment {
404 obj.insert("QUAL_ERFRAG_CODE".to_string(), json!(frag.to_uppercase()));
405 }
406 if let Some(disq) = params.disqualifier {
407 obj.insert("DISQ_ERFRAG_CODE".to_string(), json!(disq.to_uppercase()));
408 }
409 if let Some(tier) = params.tier {
410 obj.insert("ERRULE_TIER".to_string(), json!(tier));
411 }
412 }
413
414 helpers::update_in_config_array(
416 config_json,
417 "CFG_ERRULE",
418 "ERRULE_CODE",
419 &code,
420 updated_item,
421 )
422}