limbo/bindings/go/rs_src/rows.rs
2025-06-29 09:59:17 +03:00

140 lines
3.9 KiB
Rust

use crate::{
types::{LimboValue, ResultCode},
LimboConn,
};
use std::ffi::{c_char, c_void};
use turso_core::{LimboError, Statement, StepResult, Value};
pub struct LimboRows<'conn> {
stmt: Box<Statement>,
conn: &'conn mut LimboConn,
err: Option<LimboError>,
}
impl<'conn> LimboRows<'conn> {
pub fn new(stmt: Statement, conn: &'conn mut LimboConn) -> Self {
LimboRows {
stmt: Box::new(stmt),
conn,
err: None,
}
}
#[allow(clippy::wrong_self_convention)]
pub fn to_ptr(self) -> *mut c_void {
Box::into_raw(Box::new(self)) as *mut c_void
}
pub fn from_ptr(ptr: *mut c_void) -> &'conn mut LimboRows<'conn> {
if ptr.is_null() {
panic!("Null pointer");
}
unsafe { &mut *(ptr as *mut LimboRows) }
}
fn get_error(&mut self) -> *const c_char {
if let Some(err) = &self.err {
let err = format!("{}", err);
let c_str = std::ffi::CString::new(err).unwrap();
self.err = None;
c_str.into_raw() as *const c_char
} else {
std::ptr::null()
}
}
}
#[no_mangle]
pub extern "C" fn rows_next(ctx: *mut c_void) -> ResultCode {
if ctx.is_null() {
return ResultCode::Error;
}
let ctx = LimboRows::from_ptr(ctx);
match ctx.stmt.step() {
Ok(StepResult::Row) => ResultCode::Row,
Ok(StepResult::Done) => ResultCode::Done,
Ok(StepResult::IO) => {
let _ = ctx.conn.io.run_once();
ResultCode::Io
}
Ok(StepResult::Busy) => ResultCode::Busy,
Ok(StepResult::Interrupt) => ResultCode::Interrupt,
Err(err) => {
ctx.err = Some(err);
ResultCode::Error
}
}
}
#[no_mangle]
pub extern "C" fn rows_get_value(ctx: *mut c_void, col_idx: usize) -> *const c_void {
if ctx.is_null() {
return std::ptr::null();
}
let ctx = LimboRows::from_ptr(ctx);
if let Some(row) = ctx.stmt.row() {
if let Ok(value) = row.get::<&Value>(col_idx) {
return LimboValue::from_owned_value(value).to_ptr();
}
}
std::ptr::null()
}
#[no_mangle]
pub extern "C" fn free_string(s: *mut c_char) {
if !s.is_null() {
unsafe { drop(std::ffi::CString::from_raw(s)) };
}
}
/// Function to get the number of expected ResultColumns in the prepared statement.
/// to avoid the needless complexity of returning an array of strings, this instead
/// works like rows_next/rows_get_value
#[no_mangle]
pub extern "C" fn rows_get_columns(rows_ptr: *mut c_void) -> i32 {
if rows_ptr.is_null() {
return -1;
}
let rows = LimboRows::from_ptr(rows_ptr);
rows.stmt.num_columns() as i32
}
/// Returns a pointer to a string with the name of the column at the given index.
/// The caller is responsible for freeing the memory, it should be copied on the Go side
/// immediately and 'free_string' called
#[no_mangle]
pub extern "C" fn rows_get_column_name(rows_ptr: *mut c_void, idx: i32) -> *const c_char {
if rows_ptr.is_null() {
return std::ptr::null_mut();
}
let rows = LimboRows::from_ptr(rows_ptr);
if idx < 0 || idx as usize >= rows.stmt.num_columns() {
return std::ptr::null_mut();
}
let name = rows.stmt.get_column_name(idx as usize);
let cstr = std::ffi::CString::new(name.as_bytes()).expect("Failed to create CString");
cstr.into_raw() as *const c_char
}
#[no_mangle]
pub extern "C" fn rows_get_error(ctx: *mut c_void) -> *const c_char {
if ctx.is_null() {
return std::ptr::null();
}
let ctx = LimboRows::from_ptr(ctx);
ctx.get_error()
}
#[no_mangle]
pub extern "C" fn rows_close(ctx: *mut c_void) {
if !ctx.is_null() {
let rows = LimboRows::from_ptr(ctx);
rows.stmt.reset();
rows.err = None;
}
unsafe {
let _ = Box::from_raw(ctx.cast::<LimboRows>());
}
}