sz_rust_sdk/core/
engine.rs

1//! Core implementation of SzEngine trait
2
3use crate::{
4    error::{SzError, SzResult},
5    ffi_call,
6    flags::SzFlags,
7    process_engine_result,
8    traits::SzEngine,
9    types::*,
10};
11use std::collections::HashSet;
12
13/// Core implementation of the SzEngine trait
14pub struct SzEngineCore;
15
16impl SzEngineCore {
17    pub fn new() -> SzResult<Self> {
18        Ok(Self)
19    }
20}
21
22impl SzEngine for SzEngineCore {
23    fn prime_engine(&self) -> SzResult<()> {
24        ffi_call!(crate::ffi::Sz_primeEngine());
25        Ok(())
26    }
27
28    fn get_stats(&self) -> SzResult<JsonString> {
29        let result = unsafe { crate::ffi::Sz_stats_helper() };
30        process_engine_result!(result)
31    }
32
33    fn add_record(
34        &self,
35        data_source_code: &str,
36        record_id: &str,
37        record_definition: &str,
38        flags: Option<SzFlags>,
39    ) -> SzResult<JsonString> {
40        let data_source_c = crate::ffi::helpers::str_to_c_string(data_source_code)?;
41        let record_id_c = crate::ffi::helpers::str_to_c_string(record_id)?;
42        let record_def_c = crate::ffi::helpers::str_to_c_string(record_definition)?;
43        let flags_bits = flags.unwrap_or(SzFlags::ADD_RECORD_DEFAULT_FLAGS).bits() as i64;
44
45        let result = unsafe {
46            crate::ffi::Sz_addRecordWithInfo_helper(
47                data_source_c.as_ptr(),
48                record_id_c.as_ptr(),
49                record_def_c.as_ptr(),
50                flags_bits,
51            )
52        };
53
54        process_engine_result!(result)
55    }
56
57    fn get_record_preview(
58        &self,
59        record_definition: &str,
60        flags: Option<SzFlags>,
61    ) -> SzResult<JsonString> {
62        let record_def_c = crate::ffi::helpers::str_to_c_string(record_definition)?;
63        let flags_bits = flags.unwrap_or(SzFlags::RECORD_DEFAULT_FLAGS).bits() as i64;
64
65        let result =
66            unsafe { crate::ffi::Sz_getRecordPreview_helper(record_def_c.as_ptr(), flags_bits) };
67
68        process_engine_result!(result)
69    }
70
71    fn delete_record(
72        &self,
73        data_source_code: &str,
74        record_id: &str,
75        flags: Option<SzFlags>,
76    ) -> SzResult<JsonString> {
77        let data_source_c = crate::ffi::helpers::str_to_c_string(data_source_code)?;
78        let record_id_c = crate::ffi::helpers::str_to_c_string(record_id)?;
79        let flags_bits = flags.unwrap_or(SzFlags::DELETE_RECORD_DEFAULT_FLAGS).bits() as i64;
80
81        let result = unsafe {
82            crate::ffi::Sz_deleteRecordWithInfo_helper(
83                data_source_c.as_ptr(),
84                record_id_c.as_ptr(),
85                flags_bits,
86            )
87        };
88
89        process_engine_result!(result)
90    }
91
92    fn reevaluate_record(
93        &self,
94        data_source_code: &str,
95        record_id: &str,
96        flags: Option<SzFlags>,
97    ) -> SzResult<JsonString> {
98        let data_source_c = crate::ffi::helpers::str_to_c_string(data_source_code)?;
99        let record_id_c = crate::ffi::helpers::str_to_c_string(record_id)?;
100        let flags_bits = flags
101            .unwrap_or(SzFlags::REEVALUATE_RECORD_DEFAULT_FLAGS)
102            .bits() as i64;
103
104        let result = unsafe {
105            crate::ffi::Sz_reevaluateRecordWithInfo_helper(
106                data_source_c.as_ptr(),
107                record_id_c.as_ptr(),
108                flags_bits,
109            )
110        };
111
112        process_engine_result!(result)
113    }
114
115    fn reevaluate_entity(
116        &self,
117        entity_id: EntityId,
118        flags: Option<SzFlags>,
119    ) -> SzResult<JsonString> {
120        let flags_bits = flags
121            .unwrap_or(SzFlags::REEVALUATE_ENTITY_DEFAULT_FLAGS)
122            .bits() as i64;
123
124        let result =
125            unsafe { crate::ffi::Sz_reevaluateEntityWithInfo_helper(entity_id, flags_bits) };
126
127        process_engine_result!(result)
128    }
129
130    fn search_by_attributes(
131        &self,
132        attributes: &str,
133        search_profile: Option<&str>,
134        flags: Option<SzFlags>,
135    ) -> SzResult<JsonString> {
136        let attributes_c = crate::ffi::helpers::str_to_c_string(attributes)?;
137        let flags_bits = flags
138            .unwrap_or(SzFlags::SEARCH_BY_ATTRIBUTES_DEFAULT_FLAGS)
139            .bits() as i64;
140
141        // V2 and V3 have different result types, so handle separately
142        if let Some(profile) = search_profile {
143            let search_profile_c = crate::ffi::helpers::str_to_c_string(profile)?;
144            let result = unsafe {
145                crate::ffi::Sz_searchByAttributes_V3_helper(
146                    attributes_c.as_ptr(),
147                    search_profile_c.as_ptr(),
148                    flags_bits,
149                )
150            };
151            process_engine_result!(result)
152        } else {
153            let result = unsafe {
154                crate::ffi::Sz_searchByAttributes_V2_helper(attributes_c.as_ptr(), flags_bits)
155            };
156            process_engine_result!(result)
157        }
158    }
159
160    fn why_search(
161        &self,
162        attributes: &str,
163        entity_id: EntityId,
164        search_profile: Option<&str>,
165        flags: Option<SzFlags>,
166    ) -> SzResult<JsonString> {
167        let attributes_c = crate::ffi::helpers::str_to_c_string(attributes)?;
168        let search_profile_c = search_profile
169            .map(crate::ffi::helpers::str_to_c_string)
170            .transpose()?;
171        let search_profile_ptr = search_profile_c
172            .as_ref()
173            .map(|c_str| c_str.as_ptr())
174            .unwrap_or(std::ptr::null());
175        let flags_bits = flags.unwrap_or(SzFlags::WHY_SEARCH_DEFAULT_FLAGS).bits() as i64;
176
177        let result = unsafe {
178            crate::ffi::Sz_whySearch_V2_helper(
179                attributes_c.as_ptr(),
180                entity_id,
181                search_profile_ptr,
182                flags_bits,
183            )
184        };
185
186        process_engine_result!(result)
187    }
188
189    fn get_entity(&self, entity_ref: EntityRef, flags: Option<SzFlags>) -> SzResult<JsonString> {
190        let flags_bits = flags.unwrap_or(SzFlags::ENTITY_DEFAULT_FLAGS).bits() as i64;
191
192        match entity_ref {
193            EntityRef::Id(entity_id) => {
194                let result =
195                    unsafe { crate::ffi::Sz_getEntityByEntityID_V2_helper(entity_id, flags_bits) };
196                process_engine_result!(result)
197            }
198            EntityRef::Record {
199                data_source,
200                record_id,
201            } => {
202                let data_source_c = crate::ffi::helpers::str_to_c_string(data_source)?;
203                let record_id_c = crate::ffi::helpers::str_to_c_string(record_id)?;
204                let result = unsafe {
205                    crate::ffi::Sz_getEntityByRecordID_V2_helper(
206                        data_source_c.as_ptr(),
207                        record_id_c.as_ptr(),
208                        flags_bits,
209                    )
210                };
211                process_engine_result!(result)
212            }
213        }
214    }
215
216    fn get_record(
217        &self,
218        data_source_code: &str,
219        record_id: &str,
220        flags: Option<SzFlags>,
221    ) -> SzResult<JsonString> {
222        let data_source_c = crate::ffi::helpers::str_to_c_string(data_source_code)?;
223        let record_id_c = crate::ffi::helpers::str_to_c_string(record_id)?;
224        let flags_bits = flags.unwrap_or(SzFlags::RECORD_DEFAULT_FLAGS).bits() as i64;
225
226        // Use V2 helper which accepts flags
227        let result = unsafe {
228            crate::ffi::Sz_getRecord_V2_helper(
229                data_source_c.as_ptr(),
230                record_id_c.as_ptr(),
231                flags_bits,
232            )
233        };
234
235        process_engine_result!(result)
236    }
237
238    fn find_interesting_entities(
239        &self,
240        entity_ref: EntityRef,
241        flags: Option<SzFlags>,
242    ) -> SzResult<JsonString> {
243        let flags_bits = flags
244            .unwrap_or(SzFlags::FIND_INTERESTING_ENTITIES_DEFAULT_FLAGS)
245            .bits() as i64;
246
247        match entity_ref {
248            EntityRef::Id(entity_id) => {
249                let result = unsafe {
250                    crate::ffi::Sz_findInterestingEntitiesByEntityID_helper(entity_id, flags_bits)
251                };
252                process_engine_result!(result)
253            }
254            EntityRef::Record {
255                data_source,
256                record_id,
257            } => {
258                let data_source_c = crate::ffi::helpers::str_to_c_string(data_source)?;
259                let record_id_c = crate::ffi::helpers::str_to_c_string(record_id)?;
260                let result = unsafe {
261                    crate::ffi::Sz_findInterestingEntitiesByRecordID_helper(
262                        data_source_c.as_ptr(),
263                        record_id_c.as_ptr(),
264                        flags_bits,
265                    )
266                };
267                process_engine_result!(result)
268            }
269        }
270    }
271
272    fn find_path(
273        &self,
274        start_entity_id: EntityId,
275        end_entity_id: EntityId,
276        max_degrees: i64,
277        _avoid_entity_ids: Option<&HashSet<EntityId>>,
278        _required_data_sources: Option<&HashSet<String>>,
279        flags: Option<SzFlags>,
280    ) -> SzResult<JsonString> {
281        let flags_bits = flags.unwrap_or(SzFlags::FIND_PATH_DEFAULT_FLAGS).bits() as i64;
282
283        // Use V2 helper which accepts flags
284        let result = unsafe {
285            crate::ffi::Sz_findPathByEntityID_V2_helper(
286                start_entity_id,
287                end_entity_id,
288                max_degrees,
289                flags_bits,
290            )
291        };
292
293        process_engine_result!(result)
294    }
295
296    fn find_network(
297        &self,
298        entity_list: &[EntityId],
299        max_degrees: i64,
300        build_out_degree: i64,
301        max_entities: i64,
302        flags: Option<SzFlags>,
303    ) -> SzResult<JsonString> {
304        let entity_objects: Vec<serde_json::Value> = entity_list
305            .iter()
306            .map(|&id| serde_json::json!({"ENTITY_ID": id}))
307            .collect();
308
309        let entity_list_json = serde_json::json!({
310            "ENTITIES": entity_objects
311        })
312        .to_string();
313
314        let entity_list_c = crate::ffi::helpers::str_to_c_string(&entity_list_json)?;
315        let flags_bits = flags.unwrap_or(SzFlags::FIND_NETWORK_DEFAULT_FLAGS).bits() as i64;
316
317        let result = unsafe {
318            crate::ffi::Sz_findNetworkByEntityID_V2_helper(
319                entity_list_c.as_ptr(),
320                max_degrees,
321                build_out_degree,
322                max_entities,
323                flags_bits,
324            )
325        };
326
327        process_engine_result!(result)
328    }
329
330    fn why_entities(
331        &self,
332        entity_id1: EntityId,
333        entity_id2: EntityId,
334        flags: Option<SzFlags>,
335    ) -> SzResult<JsonString> {
336        let flags_bits = flags.unwrap_or(SzFlags::WHY_ENTITIES_DEFAULT_FLAGS).bits() as i64;
337
338        // Use V2 helper which accepts flags
339        let result =
340            unsafe { crate::ffi::Sz_whyEntities_V2_helper(entity_id1, entity_id2, flags_bits) };
341
342        process_engine_result!(result)
343    }
344
345    fn why_records(
346        &self,
347        data_source_code1: &str,
348        record_id1: &str,
349        data_source_code2: &str,
350        record_id2: &str,
351        flags: Option<SzFlags>,
352    ) -> SzResult<JsonString> {
353        let data_source1_c = crate::ffi::helpers::str_to_c_string(data_source_code1)?;
354        let record_id1_c = crate::ffi::helpers::str_to_c_string(record_id1)?;
355        let data_source2_c = crate::ffi::helpers::str_to_c_string(data_source_code2)?;
356        let record_id2_c = crate::ffi::helpers::str_to_c_string(record_id2)?;
357        let flags_bits = flags.unwrap_or(SzFlags::WHY_RECORDS_DEFAULT_FLAGS).bits() as i64;
358
359        // Use V2 helper which accepts flags
360        let result = unsafe {
361            crate::ffi::Sz_whyRecords_V2_helper(
362                data_source1_c.as_ptr(),
363                record_id1_c.as_ptr(),
364                data_source2_c.as_ptr(),
365                record_id2_c.as_ptr(),
366                flags_bits,
367            )
368        };
369
370        process_engine_result!(result)
371    }
372
373    fn why_record_in_entity(
374        &self,
375        data_source_code: &str,
376        record_id: &str,
377        flags: Option<SzFlags>,
378    ) -> SzResult<JsonString> {
379        let data_source_c = crate::ffi::helpers::str_to_c_string(data_source_code)?;
380        let record_id_c = crate::ffi::helpers::str_to_c_string(record_id)?;
381        let flags_bits = flags.unwrap_or(SzFlags::WHY_RECORDS_DEFAULT_FLAGS).bits() as i64;
382
383        // Use V2 helper which accepts flags
384        let result = unsafe {
385            crate::ffi::Sz_whyRecordInEntity_V2_helper(
386                data_source_c.as_ptr(),
387                record_id_c.as_ptr(),
388                flags_bits,
389            )
390        };
391
392        process_engine_result!(result)
393    }
394
395    fn how_entity(&self, entity_id: EntityId, flags: Option<SzFlags>) -> SzResult<JsonString> {
396        let flags_bits = flags.unwrap_or(SzFlags::HOW_ENTITY_DEFAULT_FLAGS).bits() as i64;
397
398        // Use V2 helper which accepts flags
399        let result = unsafe { crate::ffi::Sz_howEntityByEntityID_V2_helper(entity_id, flags_bits) };
400
401        process_engine_result!(result)
402    }
403
404    fn get_virtual_entity(
405        &self,
406        record_keys: &[(String, String)],
407        flags: Option<SzFlags>,
408    ) -> SzResult<JsonString> {
409        if record_keys.is_empty() {
410            return Err(SzError::configuration("No record keys provided"));
411        }
412
413        let record_objects: Vec<serde_json::Value> = record_keys
414            .iter()
415            .map(|(data_source, record_id)| {
416                serde_json::json!({
417                    "DATA_SOURCE": data_source,
418                    "RECORD_ID": record_id
419                })
420            })
421            .collect();
422
423        let record_list_json = serde_json::json!({
424            "RECORDS": record_objects
425        })
426        .to_string();
427
428        let record_list_c = crate::ffi::helpers::str_to_c_string(&record_list_json)?;
429        let flags_bits = flags
430            .unwrap_or(SzFlags::VIRTUAL_ENTITY_DEFAULT_FLAGS)
431            .bits() as i64;
432
433        let result = unsafe {
434            crate::ffi::Sz_getVirtualEntityByRecordID_V2_helper(record_list_c.as_ptr(), flags_bits)
435        };
436
437        process_engine_result!(result)
438    }
439
440    fn process_redo_record(
441        &self,
442        redo_record: &str,
443        _flags: Option<SzFlags>,
444    ) -> SzResult<JsonString> {
445        let redo_record_c = crate::ffi::helpers::str_to_c_string(redo_record)?;
446        // Note: Sz_processRedoRecordWithInfo_helper does not accept flags in the C API
447        let result =
448            unsafe { crate::ffi::Sz_processRedoRecordWithInfo_helper(redo_record_c.as_ptr()) };
449
450        process_engine_result!(result)
451    }
452
453    fn get_redo_record(&self) -> SzResult<JsonString> {
454        let result = unsafe { crate::ffi::Sz_getRedoRecord_helper() };
455        process_engine_result!(result)
456    }
457
458    fn count_redo_records(&self) -> SzResult<i64> {
459        let count = unsafe { crate::ffi::Sz_countRedoRecords() };
460        Ok(count)
461    }
462
463    fn export_json_entity_report(&self, flags: Option<SzFlags>) -> SzResult<ExportHandle> {
464        let flags_bits = flags.unwrap_or_default().bits() as i64;
465
466        let result = unsafe { crate::ffi::Sz_exportJSONEntityReport_helper(flags_bits) };
467
468        crate::ffi::helpers::check_return_code(result.returnCode)?;
469        Ok(result.exportHandle as ExportHandle)
470    }
471
472    fn export_csv_entity_report(
473        &self,
474        csv_column_list: &str,
475        flags: Option<SzFlags>,
476    ) -> SzResult<ExportHandle> {
477        let csv_columns_c = crate::ffi::helpers::str_to_c_string(csv_column_list)?;
478        let flags_bits = flags.unwrap_or_default().bits() as i64;
479
480        let result = unsafe {
481            crate::ffi::Sz_exportCSVEntityReport_helper(csv_columns_c.as_ptr(), flags_bits)
482        };
483
484        crate::ffi::helpers::check_return_code(result.returnCode)?;
485        Ok(result.exportHandle as ExportHandle)
486    }
487
488    fn fetch_next(&self, export_handle: ExportHandle) -> SzResult<JsonString> {
489        let result = unsafe { crate::ffi::Sz_fetchNext_helper(export_handle as usize) };
490
491        process_engine_result!(result)
492    }
493
494    fn close_export(&self, export_handle: ExportHandle) -> SzResult<()> {
495        ffi_call!(crate::ffi::Sz_closeExportReport_helper(
496            export_handle as usize
497        ));
498        Ok(())
499    }
500}