use crate::embedded::c_api::error::linkrs_error_code_t;
use crate::embedded::c_api::types::linkrs_result_t;
use crate::embedded::result::QueryResult;
use std::ffi::{c_char, c_int, CStr, CString};
use std::ptr;
pub struct LinkrsResultHandle {
pub(crate) inner: QueryResult,
}
fn value_as_i64(value: &linkrs_core::Value) -> Option<i64> {
match value {
linkrs_core::Value::SmallInt(v) => Some(*v as i64),
linkrs_core::Value::Int(v) => Some(*v as i64),
linkrs_core::Value::BigInt(v) => Some(*v),
_ => None,
}
}
fn value_as_f64(value: &linkrs_core::Value) -> Option<f64> {
match value {
linkrs_core::Value::Float(v) => Some(*v as f64),
linkrs_core::Value::Double(v) => Some(*v),
_ => None,
}
}
fn value_as_str(value: &linkrs_core::Value) -> Option<&str> {
match value {
linkrs_core::Value::String(s) => Some(s.as_str()),
linkrs_core::Value::FixedString(s) => Some(s.as_str()),
_ => None,
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_result_free(result: *mut linkrs_result_t) -> c_int {
if result.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let _ = Box::from_raw(result as *mut LinkrsResultHandle);
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_column_count(result: *mut linkrs_result_t) -> c_int {
if result.is_null() {
return -1;
}
let handle = &*(result as *mut LinkrsResultHandle);
handle.inner.columns().len() as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_row_count(result: *mut linkrs_result_t) -> c_int {
if result.is_null() {
return -1;
}
let handle = &*(result as *mut LinkrsResultHandle);
handle.inner.len() as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_column_name(
result: *mut linkrs_result_t,
index: c_int,
) -> *mut c_char {
if result.is_null() {
return ptr::null_mut();
}
let handle = &*(result as *mut LinkrsResultHandle);
match handle.inner.columns().get(index as usize) {
Some(name) => match CString::new(name.as_str()) {
Ok(c_name) => c_name.into_raw(),
Err(_) => ptr::null_mut(),
},
None => ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_get_int(
result: *mut linkrs_result_t,
row: c_int,
col: *const c_char,
value: *mut i64,
) -> c_int {
if result.is_null() || col.is_null() || value.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let col_str = match CStr::from_ptr(col).to_str() {
Ok(s) => s,
Err(_) => return linkrs_error_code_t::LINKRS_MISUSE as c_int,
};
let handle = &*(result as *mut LinkrsResultHandle);
match handle.inner.get(row as usize) {
Some(row_data) => match row_data.get(col_str) {
Some(v) => match value_as_i64(v) {
Some(i) => {
*value = i;
linkrs_error_code_t::LINKRS_OK as c_int
}
None => linkrs_error_code_t::LINKRS_MISMATCH as c_int,
},
None => linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
},
None => linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_get_string(
result: *mut linkrs_result_t,
row: c_int,
col: *const c_char,
len: *mut c_int,
) -> *mut c_char {
if result.is_null() || col.is_null() {
if !len.is_null() {
*len = -1;
}
return ptr::null_mut();
}
let col_str = match CStr::from_ptr(col).to_str() {
Ok(s) => s,
Err(_) => {
if !len.is_null() {
*len = -1;
}
return ptr::null_mut();
}
};
let handle = &*(result as *mut LinkrsResultHandle);
match handle.inner.get(row as usize) {
Some(row_data) => match row_data.get(col_str) {
Some(v) => match value_as_str(v) {
Some(s) => {
if !len.is_null() {
*len = s.len() as c_int;
}
match CString::new(s) {
Ok(c_str) => c_str.into_raw(),
Err(_) => ptr::null_mut(),
}
}
None => {
if !len.is_null() {
*len = -1;
}
ptr::null_mut()
}
},
None => ptr::null_mut(),
},
None => ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_get_blob(
result: *mut linkrs_result_t,
row: c_int,
col: *const c_char,
len: *mut c_int,
) -> *const u8 {
if result.is_null() || col.is_null() {
if !len.is_null() {
*len = -1;
}
return ptr::null();
}
let col_str = match CStr::from_ptr(col).to_str() {
Ok(s) => s,
Err(_) => {
if !len.is_null() {
*len = -1;
}
return ptr::null();
}
};
let handle = &*(result as *mut LinkrsResultHandle);
match handle.inner.get(row as usize) {
Some(row_data) => match row_data.get(col_str) {
Some(linkrs_core::Value::Blob(blob)) => {
if !len.is_null() {
*len = blob.len() as c_int;
}
blob.as_ptr()
}
Some(_) => {
if !len.is_null() {
*len = -1;
}
ptr::null()
}
None => {
if !len.is_null() {
*len = -1;
}
ptr::null()
}
},
None => {
if !len.is_null() {
*len = -1;
}
ptr::null()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_get_int_by_index(
result: *mut linkrs_result_t,
row: c_int,
col: c_int,
value: *mut i64,
) -> c_int {
if result.is_null() || value.is_null() || col < 0 {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let handle = &*(result as *mut LinkrsResultHandle);
let columns = handle.inner.columns();
let col_name = match columns.get(col as usize) {
Some(name) => name.as_str(),
None => return linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
};
match handle.inner.get(row as usize) {
Some(row_data) => match row_data.get(col_name) {
Some(v) => match value_as_i64(v) {
Some(i) => {
*value = i;
linkrs_error_code_t::LINKRS_OK as c_int
}
None => linkrs_error_code_t::LINKRS_MISMATCH as c_int,
},
None => linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
},
None => linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_get_string_by_index(
result: *mut linkrs_result_t,
row: c_int,
col: c_int,
len: *mut c_int,
) -> *mut c_char {
if result.is_null() || col < 0 {
if !len.is_null() {
*len = -1;
}
return ptr::null_mut();
}
let handle = &*(result as *mut LinkrsResultHandle);
let columns = handle.inner.columns();
let col_name = match columns.get(col as usize) {
Some(name) => name.as_str(),
None => {
if !len.is_null() {
*len = -1;
}
return ptr::null_mut();
}
};
match handle.inner.get(row as usize) {
Some(row_data) => match row_data.get(col_name) {
Some(v) => match value_as_str(v) {
Some(s) => {
if !len.is_null() {
*len = s.len() as c_int;
}
match CString::new(s) {
Ok(c_str) => c_str.into_raw(),
Err(_) => ptr::null_mut(),
}
}
None => {
if !len.is_null() {
*len = -1;
}
ptr::null_mut()
}
},
None => {
if !len.is_null() {
*len = -1;
}
ptr::null_mut()
}
},
None => {
if !len.is_null() {
*len = -1;
}
ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_get_bool_by_index(
result: *mut linkrs_result_t,
row: c_int,
col: c_int,
value: *mut bool,
) -> c_int {
if result.is_null() || value.is_null() || col < 0 {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let handle = &*(result as *mut LinkrsResultHandle);
let columns = handle.inner.columns();
let col_name = match columns.get(col as usize) {
Some(name) => name.as_str(),
None => return linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
};
match handle.inner.get(row as usize) {
Some(row_data) => match row_data.get(col_name) {
Some(linkrs_core::Value::Bool(b)) => {
*value = *b;
linkrs_error_code_t::LINKRS_OK as c_int
}
Some(_) => linkrs_error_code_t::LINKRS_MISMATCH as c_int,
None => linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
},
None => linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_get_float_by_index(
result: *mut linkrs_result_t,
row: c_int,
col: c_int,
value: *mut f64,
) -> c_int {
if result.is_null() || value.is_null() || col < 0 {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let handle = &*(result as *mut LinkrsResultHandle);
let columns = handle.inner.columns();
let col_name = match columns.get(col as usize) {
Some(name) => name.as_str(),
None => return linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
};
match handle.inner.get(row as usize) {
Some(row_data) => match row_data.get(col_name) {
Some(v) => match value_as_f64(v) {
Some(f) => {
*value = f;
linkrs_error_code_t::LINKRS_OK as c_int
}
None => linkrs_error_code_t::LINKRS_MISMATCH as c_int,
},
None => linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
},
None => linkrs_error_code_t::LINKRS_NOTFOUND as c_int,
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_get_blob_by_index(
result: *mut linkrs_result_t,
row: c_int,
col: c_int,
len: *mut c_int,
) -> *const u8 {
if result.is_null() || col < 0 {
if !len.is_null() {
*len = -1;
}
return ptr::null();
}
let handle = &*(result as *mut LinkrsResultHandle);
let columns = handle.inner.columns();
let col_name = match columns.get(col as usize) {
Some(name) => name.as_str(),
None => {
if !len.is_null() {
*len = -1;
}
return ptr::null();
}
};
match handle.inner.get(row as usize) {
Some(row_data) => match row_data.get(col_name) {
Some(linkrs_core::Value::Blob(blob)) => {
if !len.is_null() {
*len = blob.len() as c_int;
}
blob.as_ptr()
}
Some(_) => {
if !len.is_null() {
*len = -1;
}
ptr::null()
}
None => {
if !len.is_null() {
*len = -1;
}
ptr::null()
}
},
None => {
if !len.is_null() {
*len = -1;
}
ptr::null()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_column_type(
result: *mut linkrs_result_t,
col: c_int,
) -> crate::embedded::c_api::types::linkrs_value_type_t {
use crate::embedded::c_api::types::linkrs_value_type_t;
if result.is_null() || col < 0 {
return linkrs_value_type_t::LINKRS_NULL;
}
let handle = &*(result as *mut LinkrsResultHandle);
match handle.inner.first() {
Some(row) => {
let columns = handle.inner.columns();
let col_name = match columns.get(col as usize) {
Some(name) => name.as_str(),
None => return linkrs_value_type_t::LINKRS_NULL,
};
match row.get(col_name) {
Some(value) => match value {
linkrs_core::Value::Null(_) => linkrs_value_type_t::LINKRS_NULL,
linkrs_core::Value::Bool(_) => linkrs_value_type_t::LINKRS_BOOL,
linkrs_core::Value::SmallInt(_)
| linkrs_core::Value::Int(_)
| linkrs_core::Value::BigInt(_) => linkrs_value_type_t::LINKRS_INT,
linkrs_core::Value::Float(_) | linkrs_core::Value::Double(_) => {
linkrs_value_type_t::LINKRS_FLOAT
}
linkrs_core::Value::String(_) | linkrs_core::Value::FixedString(_) => {
linkrs_value_type_t::LINKRS_STRING
}
linkrs_core::Value::Blob(_) => linkrs_value_type_t::LINKRS_BLOB,
linkrs_core::Value::List(_) => linkrs_value_type_t::LINKRS_LIST,
linkrs_core::Value::Map(_) => linkrs_value_type_t::LINKRS_MAP,
linkrs_core::Value::Vertex(_) => linkrs_value_type_t::LINKRS_VERTEX,
linkrs_core::Value::Edge(_) | linkrs_core::Value::EdgeHeader(_) => {
linkrs_value_type_t::LINKRS_EDGE
}
linkrs_core::Value::Path(_) => linkrs_value_type_t::LINKRS_PATH,
_ => linkrs_value_type_t::LINKRS_NULL,
},
None => linkrs_value_type_t::LINKRS_NULL,
}
}
None => linkrs_value_type_t::LINKRS_NULL,
}
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_result_execution_time_ms(
result: *mut linkrs_result_t,
out_ms: *mut u64,
) -> c_int {
if result.is_null() || out_ms.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let handle = &*(result as *mut LinkrsResultHandle);
*out_ms = handle.inner.metadata().execution_time.as_millis() as u64;
linkrs_error_code_t::LINKRS_OK as c_int
}
#[no_mangle]
pub unsafe extern "C" fn linkrs_result_rows_scanned(
result: *mut linkrs_result_t,
out_rows: *mut u64,
) -> c_int {
if result.is_null() || out_rows.is_null() {
return linkrs_error_code_t::LINKRS_MISUSE as c_int;
}
let handle = &*(result as *mut LinkrsResultHandle);
*out_rows = handle.inner.metadata().rows_scanned;
linkrs_error_code_t::LINKRS_OK as c_int
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_result_null_params() {
let rc = unsafe { linkrs_result_free(ptr::null_mut()) };
assert_eq!(rc, linkrs_error_code_t::LINKRS_MISUSE as c_int);
let count = unsafe { linkrs_column_count(ptr::null_mut()) };
assert_eq!(count, -1);
let count = unsafe { linkrs_row_count(ptr::null_mut()) };
assert_eq!(count, -1);
let name = unsafe { linkrs_column_name(ptr::null_mut(), 0) };
assert!(name.is_null());
}
}