#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
#![allow(dead_code)]
use crate::{value::Blob, Error};
pub use linking::*;
pub use sqlite3types::*;
use std::{
ffi::{c_void, CString},
os::raw::{c_char, c_int},
ptr,
};
mod sqlite3funcs;
mod sqlite3types;
mod linking {
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::too_many_arguments)]
include!(concat!(env!("OUT_DIR"), "/linking.rs"));
}
#[cfg(modern_sqlite)]
#[macro_export]
#[doc(hidden)]
macro_rules! sqlite3_match_version_trampoline {
($($tail:tt)*) => { $crate::sqlite3_match_version!(@modern () $($tail)*) };
}
#[cfg(not(modern_sqlite))]
#[macro_export]
#[doc(hidden)]
macro_rules! sqlite3_match_version_trampoline {
($($tail:tt)*) => { $crate::sqlite3_match_version!(@old $($tail)*) };
}
#[macro_export]
macro_rules! sqlite3_match_version {
(@modern ($($body:tt)*) $ver:literal => { $($block:tt)* } $($tail:tt)* ) => {
$crate::sqlite3_match_version!(
@modern ( $($body)* $ver.. => {
$crate::sqlite3_match_version!(@verify $ver);
$($block)*
})
$($tail)*
)
};
(@old $ver:literal => { $($block:tt)* } $($tail:tt)* ) => {{
$crate::sqlite3_match_version!(@verify $ver);
$crate::sqlite3_match_version!(@old $($tail)*)
}};
(@modern ($($body:tt)*) $ver:literal => $expr:expr, $($tail:tt)* ) => {
$crate::sqlite3_match_version!(
@modern ( $($body)* $ver.. => {
$crate::sqlite3_match_version!(@verify $ver);
$expr
})
$($tail)*
)
};
(@old $ver:literal => $expr:expr, $($tail:tt)* ) => {{
$crate::sqlite3_match_version!(@verify $ver);
$crate::sqlite3_match_version!(@old $($tail)*)
}};
(@modern ($($body:tt)*) $ver:literal => $expr:expr ) => {
compile_error!("non-exhaustive patterns: missing a wildcard pattern");
};
(@old $ver:literal => $expr:expr ) => {
compile_error!("non-exhaustive patterns: missing a wildcard pattern");
};
(@modern ($($body:tt)*) _ => $expr:expr $(,)? ) => {
match $crate::SQLITE_VERSION.as_i32() {
$($body)*
_ => $expr
}
};
(@old _ => $expr:expr $(,)? ) => {
$expr
};
(@modern ($($body:tt)*) , $($tail:tt)* ) => {
$crate::sqlite3_match_version!(@modern ( $($body)* ) $($tail)*)
};
(@old , $($tail:tt)* ) => {
$crate::sqlite3_match_version!(@old $($tail)*)
};
(@verify $version:literal) => {
#[cfg(debug_assertions)]
const _: () = {
assert!($version >= 3_006_008, stringify!($version is earlier than 3.6.8 (the minimum supported version of SQLite)));
assert!($version < 4_000_000, stringify!($version is newer than 4.0.0 (which is not a valid version of SQLite3)));
};
};
( $x:literal => $($tail:tt)* ) => {
$crate::sqlite3_match_version_trampoline!($x => $($tail)*)
};
}
#[macro_export]
macro_rules! sqlite3_require_version {
($version:literal) => {
$crate::sqlite3_require_version!($version, Ok(()))
};
($version:literal, $expr:expr) => {
$crate::sqlite3_match_version! {
$version => {
let ret: Result<_> = $expr;
ret
}
_ => Err(Error::VersionNotSatisfied($version)),
}
};
}
pub const unsafe fn sqlite_transient() -> Option<unsafe extern "C" fn(arg1: *mut c_void)> {
std::mem::transmute(-1_isize as usize)
}
pub fn str_to_sqlite3(val: &str) -> Result<*mut c_char, Error> {
if val.is_empty() {
return Ok(ptr::null_mut());
}
let len: usize = val.len().checked_add(1).ok_or(crate::types::SQLITE_NOMEM)?;
unsafe {
let ptr: *mut c_char = sqlite3_match_version! {
3_008_007 => sqlite3_malloc64(len as _) as _,
_ => sqlite3_malloc(len as _) as _,
};
if !ptr.is_null() {
ptr::copy_nonoverlapping(val.as_ptr(), ptr as _, len as _);
*ptr.add(len - 1) = 0;
Ok(ptr)
} else {
Err(crate::types::SQLITE_NOMEM)
}
}
}
#[doc(hidden)]
pub unsafe fn handle_error(err: impl Into<Error>, msg: *mut *mut c_char) -> c_int {
match err.into() {
Error::Sqlite(code, s) => {
if let Some(s) = s {
if let Ok(s) = str_to_sqlite3(&s) {
unsafe { *msg = s };
}
}
code
}
e @ Error::Utf8Error(_)
| e @ Error::NulError(_)
| e @ Error::VersionNotSatisfied(_)
| e @ Error::Module(_)
| e @ Error::NoChange => {
if !msg.is_null() {
if let Ok(s) = str_to_sqlite3(&format!("{e}")) {
unsafe { *msg = s };
}
}
SQLITE_ERROR
}
}
}
#[doc(hidden)]
pub unsafe fn handle_result(result: Result<(), Error>, msg: *mut *mut c_char) -> c_int {
match result {
Ok(_) => SQLITE_OK,
Err(e) => handle_error(e, msg),
}
}
pub fn is_version(min: c_int) -> bool {
let found = unsafe { sqlite3_libversion_number() };
found >= min
}
#[doc(hidden)]
pub unsafe extern "C" fn drop_boxed<T>(data: *mut c_void) {
drop(Box::<T>::from_raw(data as _));
}
#[doc(hidden)]
pub unsafe extern "C" fn drop_cstring(data: *mut c_void) {
drop(CString::from_raw(data as _));
}
#[doc(hidden)]
pub unsafe extern "C" fn drop_blob(data: *mut c_void) {
drop(Blob::from_raw(data));
}
#[cfg(test)]
mod test {
use crate::sqlite3_match_version;
fn test_patterns() {
let s = sqlite3_match_version! {
3_008_008 => "expr,",
3_008_007 => { "{expr}" }
3_008_006 => { "{expr}," },
_ => "fall,",
};
assert_eq!(s, "expr,");
let s = sqlite3_match_version! {
3_008_006 => "expr,",
_ => "fall"
};
assert_eq!(s, "expr,");
}
}