#![allow(unsafe_code)]
use core::ffi::c_void;
use super::report::report_bool;
use crate::panic_guard::FfiBoundary;
use crate::runtime::EngineContext;
unsafe extern "C" {
fn mysql__mysql_string__set(buf: *mut c_void, bytes: *const u8, len: usize);
}
fn write_cstr_bounded(buf: *mut u8, cap: u32, s: &str) {
let cap = cap as usize;
if buf.is_null() || cap == 0 {
return;
}
let bytes = s.as_bytes();
let n = bytes.len().min(cap - 1);
unsafe {
core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, n);
*buf.add(n) = 0;
}
}
fn report_foreign_dup_key(
table_buf: *mut u8,
table_cap: u32,
key_buf: *mut u8,
key_cap: u32,
value: Option<(String, String)>,
) -> bool {
match value {
Some((table, key)) => {
write_cstr_bounded(table_buf, table_cap, &table);
write_cstr_bounded(key_buf, key_cap, &key);
true
}
None => false,
}
}
#[doc(hidden)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust__handler__print_error(
ctx: *mut EngineContext,
error: i32,
errflag: u64,
) -> bool {
FfiBoundary::run_default(false, || {
unsafe { &mut *ctx }
.engine_mut()
.print_error(error, errflag)
})
}
#[doc(hidden)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust__handler__get_error_message(
ctx: *mut EngineContext,
error: i32,
buf: *mut c_void,
) -> bool {
FfiBoundary::run_default(false, || {
match unsafe { &mut *ctx }.engine_mut().error_message(error) {
Some((msg, temporary)) => {
unsafe { mysql__mysql_string__set(buf, msg.as_ptr(), msg.len()) };
temporary
}
None => false,
}
})
}
#[doc(hidden)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust__handler__get_foreign_dup_key(
ctx: *mut EngineContext,
table_buf: *mut u8,
table_cap: u32,
key_buf: *mut u8,
key_cap: u32,
) -> bool {
FfiBoundary::run_default(false, || {
let value = unsafe { &mut *ctx }.engine_mut().foreign_dup_key();
report_foreign_dup_key(table_buf, table_cap, key_buf, key_cap, value)
})
}
#[doc(hidden)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust__handler__is_ignorable_error(
ctx: *mut EngineContext,
error: i32,
out: *mut bool,
) -> bool {
FfiBoundary::run_default(false, || {
report_bool(
out,
unsafe { &mut *ctx }.engine_mut().is_ignorable_error(error),
)
})
}
#[doc(hidden)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust__handler__is_fatal_error(
ctx: *mut EngineContext,
error: i32,
out: *mut bool,
) -> bool {
FfiBoundary::run_default(false, || {
report_bool(out, unsafe { &mut *ctx }.engine_mut().is_fatal_error(error))
})
}
#[cfg(test)]
mod tests {
use super::{report_foreign_dup_key, write_cstr_bounded};
#[test]
fn write_cstr_bounded_truncates_and_nul_terminates() {
let mut buf = [0xFFu8; 4];
write_cstr_bounded(buf.as_mut_ptr(), 4, "abcdef");
assert_eq!(&buf, b"abc\0");
}
#[test]
fn write_cstr_bounded_writes_full_string_with_nul() {
let mut buf = [0xFFu8; 8];
write_cstr_bounded(buf.as_mut_ptr(), 8, "ab");
assert_eq!(&buf[..3], b"ab\0");
}
#[test]
fn write_cstr_bounded_tolerates_null_and_zero_cap() {
write_cstr_bounded(core::ptr::null_mut(), 4, "x");
let mut buf = [7u8; 2];
write_cstr_bounded(buf.as_mut_ptr(), 0, "x");
assert_eq!(buf, [7, 7]);
}
#[test]
fn report_foreign_dup_key_writes_both_and_signals_handled() {
let mut t = [0u8; 8];
let mut k = [0u8; 8];
let handled = report_foreign_dup_key(
t.as_mut_ptr(),
8,
k.as_mut_ptr(),
8,
Some(("tbl".to_owned(), "key".to_owned())),
);
assert!(handled);
assert_eq!(&t[..4], b"tbl\0");
assert_eq!(&k[..4], b"key\0");
}
#[test]
fn report_foreign_dup_key_none_signals_unhandled() {
assert!(!report_foreign_dup_key(
core::ptr::null_mut(),
0,
core::ptr::null_mut(),
0,
None
));
}
}