use std::ffi::CStr;
use std::os::raw::c_char;
use std::path::Path;
use crate::format::AcbReader;
use crate::graph::CodeGraph;
pub const ACB_OK: i32 = 0;
pub const ACB_ERR_IO: i32 = -1;
pub const ACB_ERR_INVALID: i32 = -2;
pub const ACB_ERR_NOT_FOUND: i32 = -3;
pub const ACB_ERR_OVERFLOW: i32 = -4;
pub const ACB_ERR_NULL_PTR: i32 = -5;
#[no_mangle]
pub unsafe extern "C" fn acb_graph_open(path: *const c_char) -> *mut std::ffi::c_void {
std::panic::catch_unwind(|| {
if path.is_null() {
return std::ptr::null_mut();
}
let path_str = unsafe { CStr::from_ptr(path) };
let path_str = match path_str.to_str() {
Ok(s) => s,
Err(_) => return std::ptr::null_mut(),
};
match AcbReader::read_from_file(Path::new(path_str)) {
Ok(graph) => Box::into_raw(Box::new(graph)) as *mut std::ffi::c_void,
Err(_) => std::ptr::null_mut(),
}
})
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_free(graph: *mut std::ffi::c_void) {
if !graph.is_null() {
let _ = std::panic::catch_unwind(|| unsafe {
drop(Box::from_raw(graph as *mut CodeGraph));
});
}
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_unit_count(graph: *mut std::ffi::c_void) -> u64 {
std::panic::catch_unwind(|| {
if graph.is_null() {
return 0;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
graph.unit_count() as u64
})
.unwrap_or(0)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_edge_count(graph: *mut std::ffi::c_void) -> u64 {
std::panic::catch_unwind(|| {
if graph.is_null() {
return 0;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
graph.edge_count() as u64
})
.unwrap_or(0)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_dimension(graph: *mut std::ffi::c_void) -> u32 {
std::panic::catch_unwind(|| {
if graph.is_null() {
return 0;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
graph.dimension() as u32
})
.unwrap_or(0)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_get_unit_name(
graph: *mut std::ffi::c_void,
unit_id: u64,
buffer: *mut c_char,
buffer_size: u32,
) -> i32 {
std::panic::catch_unwind(|| {
if graph.is_null() || buffer.is_null() {
return ACB_ERR_NULL_PTR;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
match graph.get_unit(unit_id) {
Some(unit) => {
let name_bytes = unit.name.as_bytes();
if name_bytes.len() + 1 > buffer_size as usize {
return ACB_ERR_OVERFLOW;
}
unsafe {
std::ptr::copy_nonoverlapping(
name_bytes.as_ptr(),
buffer as *mut u8,
name_bytes.len(),
);
*buffer.add(name_bytes.len()) = 0; }
name_bytes.len() as i32
}
None => ACB_ERR_NOT_FOUND,
}
})
.unwrap_or(ACB_ERR_INVALID)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_get_unit_type(
graph: *mut std::ffi::c_void,
unit_id: u64,
) -> i32 {
std::panic::catch_unwind(|| {
if graph.is_null() {
return -1;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
graph
.get_unit(unit_id)
.map(|u| u.unit_type as i32)
.unwrap_or(-1)
})
.unwrap_or(-1)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_get_unit_file(
graph: *mut std::ffi::c_void,
unit_id: u64,
buffer: *mut c_char,
buffer_size: u32,
) -> i32 {
std::panic::catch_unwind(|| {
if graph.is_null() || buffer.is_null() {
return ACB_ERR_NULL_PTR;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
match graph.get_unit(unit_id) {
Some(unit) => {
let path_str = unit.file_path.display().to_string();
let path_bytes = path_str.as_bytes();
if path_bytes.len() + 1 > buffer_size as usize {
return ACB_ERR_OVERFLOW;
}
unsafe {
std::ptr::copy_nonoverlapping(
path_bytes.as_ptr(),
buffer as *mut u8,
path_bytes.len(),
);
*buffer.add(path_bytes.len()) = 0;
}
path_bytes.len() as i32
}
None => ACB_ERR_NOT_FOUND,
}
})
.unwrap_or(ACB_ERR_INVALID)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_get_unit_complexity(
graph: *mut std::ffi::c_void,
unit_id: u64,
) -> f32 {
std::panic::catch_unwind(|| {
if graph.is_null() {
return -1.0;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
graph
.get_unit(unit_id)
.map(|u| u.complexity as f32)
.unwrap_or(-1.0)
})
.unwrap_or(-1.0)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_get_edges(
graph: *mut std::ffi::c_void,
unit_id: u64,
target_ids: *mut u64,
edge_types: *mut u8,
weights: *mut f32,
max_edges: u32,
) -> i32 {
std::panic::catch_unwind(|| {
if graph.is_null() || target_ids.is_null() || edge_types.is_null() || weights.is_null() {
return ACB_ERR_NULL_PTR;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
let edges = graph.edges_from(unit_id);
let count = edges.len().min(max_edges as usize);
for (i, edge) in edges.iter().take(count).enumerate() {
unsafe {
*target_ids.add(i) = edge.target_id;
*edge_types.add(i) = edge.edge_type as u8;
*weights.add(i) = edge.weight;
}
}
count as i32
})
.unwrap_or(ACB_ERR_INVALID)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_get_unit_language(
graph: *mut std::ffi::c_void,
unit_id: u64,
) -> i32 {
std::panic::catch_unwind(|| {
if graph.is_null() {
return -1;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
graph
.get_unit(unit_id)
.map(|u| u.language as i32)
.unwrap_or(-1)
})
.unwrap_or(-1)
}
#[no_mangle]
pub unsafe extern "C" fn acb_graph_get_unit_stability(
graph: *mut std::ffi::c_void,
unit_id: u64,
) -> f32 {
std::panic::catch_unwind(|| {
if graph.is_null() {
return -1.0;
}
let graph = unsafe { &*(graph as *const CodeGraph) };
graph
.get_unit(unit_id)
.map(|u| u.stability_score)
.unwrap_or(-1.0)
})
.unwrap_or(-1.0)
}