1use std::ffi::CStr;
4use std::os::raw::c_char;
5use std::path::Path;
6
7use crate::format::{AmemReader, AmemWriter};
8use crate::graph::MemoryGraph;
9use crate::types::{CognitiveEventBuilder, Edge, EdgeType, EventType};
10
11const AMEM_OK: i32 = 0;
12const AMEM_ERR_IO: i32 = -1;
13const AMEM_ERR_INVALID: i32 = -2;
14const AMEM_ERR_NOT_FOUND: i32 = -3;
15const AMEM_ERR_OVERFLOW: i32 = -4;
16const AMEM_ERR_NULL_PTR: i32 = -5;
17
18#[no_mangle]
20pub extern "C" fn amem_graph_new(dimension: u32) -> *mut std::ffi::c_void {
21 std::panic::catch_unwind(|| {
22 let graph = Box::new(MemoryGraph::new(dimension as usize));
23 Box::into_raw(graph) as *mut std::ffi::c_void
24 })
25 .unwrap_or(std::ptr::null_mut())
26}
27
28#[no_mangle]
36pub unsafe extern "C" fn amem_graph_open(path: *const c_char) -> *mut std::ffi::c_void {
37 std::panic::catch_unwind(|| {
38 if path.is_null() {
39 return std::ptr::null_mut();
40 }
41 let path_str = unsafe { CStr::from_ptr(path) };
42 let path_str = match path_str.to_str() {
43 Ok(s) => s,
44 Err(_) => return std::ptr::null_mut(),
45 };
46 match AmemReader::read_from_file(Path::new(path_str)) {
47 Ok(graph) => Box::into_raw(Box::new(graph)) as *mut std::ffi::c_void,
48 Err(_) => std::ptr::null_mut(),
49 }
50 })
51 .unwrap_or(std::ptr::null_mut())
52}
53
54#[no_mangle]
62pub unsafe extern "C" fn amem_graph_save(graph: *mut std::ffi::c_void, path: *const c_char) -> i32 {
63 std::panic::catch_unwind(|| {
64 if graph.is_null() || path.is_null() {
65 return AMEM_ERR_NULL_PTR;
66 }
67 let graph = unsafe { &*(graph as *const MemoryGraph) };
68 let path_str = unsafe { CStr::from_ptr(path) };
69 let path_str = match path_str.to_str() {
70 Ok(s) => s,
71 Err(_) => return AMEM_ERR_INVALID,
72 };
73 let writer = AmemWriter::new(graph.dimension());
74 match writer.write_to_file(graph, Path::new(path_str)) {
75 Ok(()) => AMEM_OK,
76 Err(_) => AMEM_ERR_IO,
77 }
78 })
79 .unwrap_or(AMEM_ERR_IO)
80}
81
82#[no_mangle]
90pub unsafe extern "C" fn amem_graph_free(graph: *mut std::ffi::c_void) {
91 if !graph.is_null() {
92 let _ = std::panic::catch_unwind(|| unsafe {
93 drop(Box::from_raw(graph as *mut MemoryGraph));
94 });
95 }
96}
97
98#[no_mangle]
106pub unsafe extern "C" fn amem_graph_node_count(graph: *mut std::ffi::c_void) -> u64 {
107 std::panic::catch_unwind(|| {
108 if graph.is_null() {
109 return 0;
110 }
111 let graph = unsafe { &*(graph as *const MemoryGraph) };
112 graph.node_count() as u64
113 })
114 .unwrap_or(0)
115}
116
117#[no_mangle]
125pub unsafe extern "C" fn amem_graph_edge_count(graph: *mut std::ffi::c_void) -> u64 {
126 std::panic::catch_unwind(|| {
127 if graph.is_null() {
128 return 0;
129 }
130 let graph = unsafe { &*(graph as *const MemoryGraph) };
131 graph.edge_count() as u64
132 })
133 .unwrap_or(0)
134}
135
136#[no_mangle]
144pub unsafe extern "C" fn amem_graph_dimension(graph: *mut std::ffi::c_void) -> u32 {
145 std::panic::catch_unwind(|| {
146 if graph.is_null() {
147 return 0;
148 }
149 let graph = unsafe { &*(graph as *const MemoryGraph) };
150 graph.dimension() as u32
151 })
152 .unwrap_or(0)
153}
154
155#[no_mangle]
163pub unsafe extern "C" fn amem_graph_add_node(
164 graph: *mut std::ffi::c_void,
165 event_type: u8,
166 content: *const c_char,
167 session_id: u32,
168 confidence: f32,
169) -> i64 {
170 std::panic::catch_unwind(|| {
171 if graph.is_null() || content.is_null() {
172 return -1i64;
173 }
174 let graph = unsafe { &mut *(graph as *mut MemoryGraph) };
175 let content_str = unsafe { CStr::from_ptr(content) };
176 let content_str = match content_str.to_str() {
177 Ok(s) => s,
178 Err(_) => return -1,
179 };
180 let et = match EventType::from_u8(event_type) {
181 Some(et) => et,
182 None => return -1,
183 };
184 let event = CognitiveEventBuilder::new(et, content_str)
185 .session_id(session_id)
186 .confidence(confidence)
187 .build();
188 match graph.add_node(event) {
189 Ok(id) => id as i64,
190 Err(_) => -1,
191 }
192 })
193 .unwrap_or(-1)
194}
195
196#[no_mangle]
204pub unsafe extern "C" fn amem_graph_get_content(
205 graph: *mut std::ffi::c_void,
206 node_id: u64,
207 buffer: *mut c_char,
208 buffer_size: u32,
209) -> i32 {
210 std::panic::catch_unwind(|| {
211 if graph.is_null() || buffer.is_null() {
212 return AMEM_ERR_NULL_PTR;
213 }
214 let graph = unsafe { &*(graph as *const MemoryGraph) };
215 match graph.get_node(node_id) {
216 Some(node) => {
217 let content_bytes = node.content.as_bytes();
218 if content_bytes.len() + 1 > buffer_size as usize {
219 return AMEM_ERR_OVERFLOW;
220 }
221 unsafe {
222 std::ptr::copy_nonoverlapping(
223 content_bytes.as_ptr(),
224 buffer as *mut u8,
225 content_bytes.len(),
226 );
227 *buffer.add(content_bytes.len()) = 0; }
229 content_bytes.len() as i32
230 }
231 None => AMEM_ERR_NOT_FOUND,
232 }
233 })
234 .unwrap_or(AMEM_ERR_INVALID)
235}
236
237#[no_mangle]
245pub unsafe extern "C" fn amem_graph_get_confidence(
246 graph: *mut std::ffi::c_void,
247 node_id: u64,
248) -> f32 {
249 std::panic::catch_unwind(|| {
250 if graph.is_null() {
251 return -1.0;
252 }
253 let graph = unsafe { &*(graph as *const MemoryGraph) };
254 graph
255 .get_node(node_id)
256 .map(|n| n.confidence)
257 .unwrap_or(-1.0)
258 })
259 .unwrap_or(-1.0)
260}
261
262#[no_mangle]
270pub unsafe extern "C" fn amem_graph_get_event_type(
271 graph: *mut std::ffi::c_void,
272 node_id: u64,
273) -> i32 {
274 std::panic::catch_unwind(|| {
275 if graph.is_null() {
276 return -1;
277 }
278 let graph = unsafe { &*(graph as *const MemoryGraph) };
279 graph
280 .get_node(node_id)
281 .map(|n| n.event_type as i32)
282 .unwrap_or(-1)
283 })
284 .unwrap_or(-1)
285}
286
287#[no_mangle]
295pub unsafe extern "C" fn amem_graph_add_edge(
296 graph: *mut std::ffi::c_void,
297 source_id: u64,
298 target_id: u64,
299 edge_type: u8,
300 weight: f32,
301) -> i32 {
302 std::panic::catch_unwind(|| {
303 if graph.is_null() {
304 return AMEM_ERR_NULL_PTR;
305 }
306 let graph = unsafe { &mut *(graph as *mut MemoryGraph) };
307 let et = match EdgeType::from_u8(edge_type) {
308 Some(et) => et,
309 None => return AMEM_ERR_INVALID,
310 };
311 let edge = Edge::new(source_id, target_id, et, weight);
312 match graph.add_edge(edge) {
313 Ok(()) => AMEM_OK,
314 Err(_) => AMEM_ERR_INVALID,
315 }
316 })
317 .unwrap_or(AMEM_ERR_INVALID)
318}
319
320#[no_mangle]
328pub unsafe extern "C" fn amem_graph_get_edges(
329 graph: *mut std::ffi::c_void,
330 source_id: u64,
331 target_ids: *mut u64,
332 edge_types: *mut u8,
333 weights: *mut f32,
334 max_edges: u32,
335) -> i32 {
336 std::panic::catch_unwind(|| {
337 if graph.is_null() || target_ids.is_null() || edge_types.is_null() || weights.is_null() {
338 return AMEM_ERR_NULL_PTR;
339 }
340 let graph = unsafe { &*(graph as *const MemoryGraph) };
341 let edges = graph.edges_from(source_id);
342 let count = edges.len().min(max_edges as usize);
343 for (i, edge) in edges.iter().take(count).enumerate() {
344 unsafe {
345 *target_ids.add(i) = edge.target_id;
346 *edge_types.add(i) = edge.edge_type as u8;
347 *weights.add(i) = edge.weight;
348 }
349 }
350 count as i32
351 })
352 .unwrap_or(AMEM_ERR_INVALID)
353}
354
355#[no_mangle]
363pub unsafe extern "C" fn amem_graph_traverse(
364 graph: *mut std::ffi::c_void,
365 start_id: u64,
366 edge_types_ptr: *const u8,
367 edge_type_count: u32,
368 direction: u8,
369 max_depth: u32,
370 visited_ids: *mut u64,
371 max_results: u32,
372) -> i32 {
373 std::panic::catch_unwind(|| {
374 if graph.is_null() || edge_types_ptr.is_null() || visited_ids.is_null() {
375 return AMEM_ERR_NULL_PTR;
376 }
377 let graph_ref = unsafe { &*(graph as *const MemoryGraph) };
378
379 let edge_types: Vec<EdgeType> = (0..edge_type_count)
380 .filter_map(|i| {
381 let val = unsafe { *edge_types_ptr.add(i as usize) };
382 EdgeType::from_u8(val)
383 })
384 .collect();
385
386 let dir = match direction {
387 0 => crate::graph::TraversalDirection::Forward,
388 1 => crate::graph::TraversalDirection::Backward,
389 _ => crate::graph::TraversalDirection::Both,
390 };
391
392 let query_engine = crate::engine::QueryEngine::new();
393 let params = crate::engine::TraversalParams {
394 start_id,
395 edge_types,
396 direction: dir,
397 max_depth,
398 max_results: max_results as usize,
399 min_confidence: 0.0,
400 };
401
402 match query_engine.traverse(graph_ref, params) {
403 Ok(result) => {
404 let count = result.visited.len().min(max_results as usize);
405 for (i, &id) in result.visited.iter().take(count).enumerate() {
406 unsafe {
407 *visited_ids.add(i) = id;
408 }
409 }
410 count as i32
411 }
412 Err(_) => AMEM_ERR_NOT_FOUND,
413 }
414 })
415 .unwrap_or(AMEM_ERR_INVALID)
416}
417
418#[no_mangle]
426pub unsafe extern "C" fn amem_graph_resolve(graph: *mut std::ffi::c_void, node_id: u64) -> i64 {
427 std::panic::catch_unwind(|| {
428 if graph.is_null() {
429 return -1i64;
430 }
431 let graph_ref = unsafe { &*(graph as *const MemoryGraph) };
432 let query_engine = crate::engine::QueryEngine::new();
433 match query_engine.resolve(graph_ref, node_id) {
434 Ok(node) => node.id as i64,
435 Err(_) => -1,
436 }
437 })
438 .unwrap_or(-1)
439}
440
441#[no_mangle]
449pub unsafe extern "C" fn amem_graph_correct(
450 graph: *mut std::ffi::c_void,
451 old_node_id: u64,
452 new_content: *const c_char,
453 session_id: u32,
454) -> i64 {
455 std::panic::catch_unwind(|| {
456 if graph.is_null() || new_content.is_null() {
457 return -1i64;
458 }
459 let graph_mut = unsafe { &mut *(graph as *mut MemoryGraph) };
460 let content_str = unsafe { CStr::from_ptr(new_content) };
461 let content_str = match content_str.to_str() {
462 Ok(s) => s,
463 Err(_) => return -1,
464 };
465 let write_engine = crate::engine::WriteEngine::new(graph_mut.dimension());
466 match write_engine.correct(graph_mut, old_node_id, content_str, session_id) {
467 Ok(id) => id as i64,
468 Err(_) => -1,
469 }
470 })
471 .unwrap_or(-1)
472}
473
474#[no_mangle]
482pub unsafe extern "C" fn amem_graph_touch(graph: *mut std::ffi::c_void, node_id: u64) -> i32 {
483 std::panic::catch_unwind(|| {
484 if graph.is_null() {
485 return AMEM_ERR_NULL_PTR;
486 }
487 let graph_mut = unsafe { &mut *(graph as *mut MemoryGraph) };
488 let write_engine = crate::engine::WriteEngine::new(graph_mut.dimension());
489 match write_engine.touch(graph_mut, node_id) {
490 Ok(()) => AMEM_OK,
491 Err(_) => AMEM_ERR_NOT_FOUND,
492 }
493 })
494 .unwrap_or(AMEM_ERR_INVALID)
495}