xml_disassembler/parsers/
parse_unique_id.rs1use serde_json::Value;
4use sha2::{Digest, Sha256};
5
6use crate::types::XmlElement;
7
8fn create_short_hash(element: &XmlElement) -> String {
11 let stringified = serde_json::to_string(element).unwrap_or_default();
12 let mut hasher = Sha256::new();
13 hasher.update(stringified.as_bytes());
14 let result = hasher.finalize();
15 format!("{:x}", result)[..8].to_string()
16}
17
18fn is_object(value: &Value) -> bool {
19 value.is_object() && !value.is_array()
20}
21
22fn value_as_string(value: &Value) -> Option<String> {
24 if let Some(s) = value.as_str() {
25 return Some(s.to_string());
26 }
27 if let Some(obj) = value.as_object() {
28 if let Some(text) = obj.get("#text").and_then(|v| v.as_str()) {
29 return Some(text.to_string());
30 }
31 }
32 None
33}
34
35fn find_direct_field_match(element: &XmlElement, field_names: &[&str]) -> Option<String> {
36 let obj = element.as_object()?;
37 for name in field_names {
38 if let Some(value) = obj.get(*name) {
39 if let Some(s) = value_as_string(value) {
40 return Some(s);
41 }
42 }
43 }
44 None
45}
46
47fn find_nested_field_match(element: &XmlElement, unique_id_elements: &str) -> Option<String> {
48 let obj = element.as_object()?;
49 for (_, child) in obj {
50 if is_object(child) {
51 let result = parse_unique_id_element(child, Some(unique_id_elements));
52 if !result.is_empty() {
53 return Some(result);
54 }
55 }
56 }
57 None
58}
59
60pub fn parse_unique_id_element(element: &XmlElement, unique_id_elements: Option<&str>) -> String {
62 if let Some(ids) = unique_id_elements {
63 let field_names: Vec<&str> = ids.split(',').map(|s| s.trim()).collect();
64 find_direct_field_match(element, &field_names)
65 .or_else(|| find_nested_field_match(element, ids))
66 .unwrap_or_else(|| create_short_hash(element))
67 } else {
68 create_short_hash(element)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use serde_json::json;
76
77 #[test]
78 fn finds_direct_field() {
79 let el = json!({ "name": "Get_Info", "label": "Get Info" });
80 assert_eq!(parse_unique_id_element(&el, Some("name")), "Get_Info");
81 }
82
83 #[test]
84 fn finds_deeply_nested_field() {
85 let el = json!({
87 "value": { "elementReference": "accts.accounts" },
88 "connector": { "targetReference": "X" }
89 });
90 assert_eq!(
91 parse_unique_id_element(&el, Some("elementReference")),
92 "accts.accounts"
93 );
94 }
95
96 #[test]
97 fn finds_id_in_grandchild() {
98 let el = json!({
99 "wrapper": {
100 "inner": { "name": "NestedName" }
101 }
102 });
103 assert_eq!(parse_unique_id_element(&el, Some("name")), "NestedName");
104 }
105
106 #[test]
107 fn finds_name_from_text_object() {
108 let el = json!({
110 "name": { "#text": "Get_Info" },
111 "label": { "#text": "Get Info" },
112 "actionName": { "#text": "GetFirstFromCollection" }
113 });
114 assert_eq!(parse_unique_id_element(&el, Some("name")), "Get_Info");
115 assert_eq!(
116 parse_unique_id_element(&el, Some("actionName")),
117 "GetFirstFromCollection"
118 );
119 }
120}