use std::ffi::c_char;
use crate::error::{Status, gpkg_error_t, set_error, set_library_error};
use crate::handle::{LayerHandle, WriterHandle};
use crate::util::borrow_str;
use crate::value::{borrow_values, gpkg_value_t};
#[expect(
non_camel_case_types,
reason = "the C name is the type's name; cbindgen emits it verbatim"
)]
pub type gpkg_writer_t = WriterHandle;
unsafe fn writer_mut<'a>(
writer: *mut gpkg_writer_t,
what: &str,
error: *mut gpkg_error_t,
) -> Option<&'a mut WriterHandle> {
if writer.is_null() {
let message = format!("{what}: writer is NULL");
unsafe { set_error(error, Status::BadArgument, &message) };
return None;
}
Some(unsafe { &mut *writer })
}
unsafe fn borrow_wkb<'a>(
wkb: *const u8,
wkb_len: usize,
error: *mut gpkg_error_t,
) -> Option<Option<&'a [u8]>> {
if wkb.is_null() {
if wkb_len != 0 {
unsafe {
set_error(
error,
Status::BadArgument,
"geometry is NULL with a non-zero length",
);
}
return None;
}
return Some(None);
}
Some(Some(unsafe { std::slice::from_raw_parts(wkb, wkb_len) }))
}
unsafe fn failed(err: &geopackage::Error, error: *mut gpkg_error_t) -> Status {
unsafe { set_library_error(error, err) };
Status::from(err)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpkg_layer_writer(
layer: *const LayerHandle,
error: *mut gpkg_error_t,
) -> *mut gpkg_writer_t {
if layer.is_null() {
unsafe {
set_error(
error,
Status::BadArgument,
"gpkg_layer_writer: layer is NULL",
)
};
return std::ptr::null_mut();
}
match unsafe { (*layer).writer() } {
Ok(writer) => Box::into_raw(Box::new(writer)),
Err(err) => {
unsafe { set_library_error(error, &err) };
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpkg_writer_commit(
writer: *mut gpkg_writer_t,
error: *mut gpkg_error_t,
) -> Status {
if writer.is_null() {
unsafe {
set_error(
error,
Status::BadArgument,
"gpkg_writer_commit: writer is NULL",
)
};
return Status::BadArgument;
}
let handle = unsafe { Box::from_raw(writer) };
match handle.commit() {
Ok(()) => Status::Ok,
Err(err) => unsafe { failed(&err, error) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpkg_writer_free(writer: *mut gpkg_writer_t) {
if writer.is_null() {
return;
}
drop(unsafe { Box::from_raw(writer) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpkg_writer_insert(
writer: *mut gpkg_writer_t,
fid: *const i64,
wkb: *const u8,
wkb_len: usize,
values: *const gpkg_value_t,
values_len: usize,
out_fid: *mut i64,
error: *mut gpkg_error_t,
) -> Status {
let Some(handle) = (unsafe { writer_mut(writer, "gpkg_writer_insert", error) }) else {
return Status::BadArgument;
};
let fid = unsafe { fid.as_ref() }.copied();
let Some(geometry) = (unsafe { borrow_wkb(wkb, wkb_len, error) }) else {
return Status::BadArgument;
};
let values = match unsafe { borrow_values(values, values_len, error) } {
Ok(values) => values,
Err(status) => return status,
};
let written = match geometry {
Some(wkb) => handle.writer_mut().insert_wkb(fid, wkb, &values),
None => handle.writer_mut().insert_row(fid, &values),
};
match written {
Ok(assigned) => {
if !out_fid.is_null() {
unsafe { *out_fid = assigned };
}
Status::Ok
}
Err(err) => unsafe { failed(&err, error) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpkg_writer_update(
writer: *mut gpkg_writer_t,
fid: i64,
wkb: *const u8,
wkb_len: usize,
values: *const gpkg_value_t,
values_len: usize,
out_matched: *mut bool,
error: *mut gpkg_error_t,
) -> Status {
let Some(handle) = (unsafe { writer_mut(writer, "gpkg_writer_update", error) }) else {
return Status::BadArgument;
};
let Some(geometry) = (unsafe { borrow_wkb(wkb, wkb_len, error) }) else {
return Status::BadArgument;
};
let values = match unsafe { borrow_values(values, values_len, error) } {
Ok(values) => values,
Err(status) => return status,
};
let updated = match geometry {
Some(wkb) => handle.writer_mut().update_wkb(fid, wkb, &values),
None => handle.writer_mut().update_row(fid, &values),
};
unsafe { report_matched(updated, out_matched, error) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpkg_writer_update_column(
writer: *mut gpkg_writer_t,
fid: i64,
column: *const c_char,
value: *const gpkg_value_t,
out_matched: *mut bool,
error: *mut gpkg_error_t,
) -> Status {
let Some(handle) = (unsafe { writer_mut(writer, "gpkg_writer_update_column", error) }) else {
return Status::BadArgument;
};
let Some(column) = (unsafe { borrow_str(column, error, "column") }) else {
return Status::BadArgument;
};
let values = match unsafe { borrow_values(value, 1, error) } {
Ok(values) => values,
Err(status) => return status,
};
let Some(cell) = values.first().copied() else {
return Status::InvalidArgument;
};
let updated = handle.writer_mut().update_column(fid, column, cell);
unsafe { report_matched(updated, out_matched, error) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn gpkg_writer_delete(
writer: *mut gpkg_writer_t,
fid: i64,
out_matched: *mut bool,
error: *mut gpkg_error_t,
) -> Status {
let Some(handle) = (unsafe { writer_mut(writer, "gpkg_writer_delete", error) }) else {
return Status::BadArgument;
};
let deleted = handle.writer_mut().delete(fid);
unsafe { report_matched(deleted, out_matched, error) }
}
unsafe fn report_matched(
outcome: geopackage::Result<bool>,
out_matched: *mut bool,
error: *mut gpkg_error_t,
) -> Status {
match outcome {
Ok(matched) => {
if !out_matched.is_null() {
unsafe { *out_matched = matched };
}
Status::Ok
}
Err(err) => unsafe { failed(&err, error) },
}
}