use crate::{DuckExtraInfo, DuckOptionResult, DuckResult, panic_to_string, raw_extra_info};
use libduckdb_sys::duckdb_replacement_scan_info;
use quack_rs::prelude::{Connection, ReplacementScanInfo};
use std::panic::catch_unwind;
pub trait ReplacementScanAdapter: Sized + 'static {
const NAME: &'static str;
fn register(c: &Connection) -> DuckResult<()> {
let (extra_data, delete_callback) = match raw_extra_info(Self::extra_info()) {
Some((ptr, destroy)) => (ptr, destroy),
None => (std::ptr::null_mut(), None),
};
unsafe {
c.register_replacement_scan(Self::scan_callback, extra_data, delete_callback);
}
Ok(())
}
fn extra_info() -> Option<DuckExtraInfo> {
None
}
unsafe extern "C" fn scan_callback(
info: duckdb_replacement_scan_info,
table_name: *const ::std::os::raw::c_char,
data: *mut ::std::os::raw::c_void,
) {
let path = unsafe { std::ffi::CStr::from_ptr(table_name) };
let Ok(path) = path.to_str() else {
return;
};
let extra = unsafe { DuckExtraInfo::from_raw(data) };
let scan_info = unsafe { ReplacementScanInfo::new(info) };
match catch_unwind(|| Self::handle_info_with_extra(&scan_info, path, extra)) {
Ok(Ok(())) => {}
Ok(Err(e)) => scan_info.set_error(e.as_str()),
Err(e) => scan_info.set_error(&panic_to_string(e)),
}
}
fn handle_info(info: &ReplacementScanInfo, path: &str) -> DuckResult<()> {
if let Some(table_fn) = Self::handle_path(path)? {
info.set_function(&table_fn).add_varchar_parameter(path);
}
Ok(())
}
fn handle_info_with_extra(
info: &ReplacementScanInfo,
path: &str,
extra: Option<&DuckExtraInfo>,
) -> DuckResult<()> {
let _ = extra;
Self::handle_info(info, path)
}
fn handle_path(path: &str) -> DuckOptionResult<String>;
}