use std::ffi::{CStr, CString, c_char};
use crate::error::{Status, gpkg_error_t, set_error};
pub(crate) unsafe fn borrow_str<'a>(
ptr: *const c_char,
error: *mut gpkg_error_t,
what: &str,
) -> Option<&'a str> {
if ptr.is_null() {
unsafe { set_error(error, Status::BadArgument, &format!("{what} is NULL")) };
return None;
}
let raw = unsafe { CStr::from_ptr(ptr) };
match raw.to_str() {
Ok(text) => Some(text),
Err(_) => {
unsafe {
set_error(
error,
Status::BadArgument,
&format!("{what} is not valid UTF-8"),
);
}
None
}
}
}
pub(crate) unsafe fn out_string(text: &str, error: *mut gpkg_error_t) -> *mut c_char {
match CString::new(text) {
Ok(owned) => owned.into_raw(),
Err(_) => {
unsafe {
set_error(
error,
Status::InvalidArgument,
"value contains an interior NUL and cannot be a C string",
);
}
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpkg_string_free(text: *mut c_char) {
if text.is_null() {
return;
}
drop(unsafe { CString::from_raw(text) });
}
pub(crate) unsafe fn write_out_strings(
slots: &[(*mut *mut c_char, Option<String>)],
error: *mut gpkg_error_t,
) -> bool {
let mut converted: Vec<*mut c_char> = Vec::with_capacity(slots.len());
for (out, text) in slots {
let wanted = match text {
Some(text) if !out.is_null() => Some(text.as_str()),
_ => None,
};
let Some(text) = wanted else {
converted.push(std::ptr::null_mut());
continue;
};
let owned = unsafe { out_string(text, error) };
if owned.is_null() {
for earlier in converted.into_iter().filter(|p| !p.is_null()) {
drop(unsafe { CString::from_raw(earlier) });
}
return false;
}
converted.push(owned);
}
for ((out, _), owned) in slots.iter().zip(converted) {
if !out.is_null() {
unsafe { *(*out) = owned };
}
}
true
}