use core::{
ffi::{c_int, c_uchar, c_void},
num::NonZero,
ptr,
};
use super::statement::Statement;
use crate::{
blob::Reservation,
error::{Error, ErrorMessage, Result},
};
use sqlite::{
SQLITE_STATIC, SQLITE_TRANSIENT, sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64,
sqlite3_bind_null, sqlite3_destructor_type,
};
#[cfg(target_pointer_width = "64")]
use sqlite::{
SQLITE_UTF8, sqlite3_bind_blob64, sqlite3_bind_text64, sqlite3_bind_zeroblob64, sqlite3_uint64,
};
#[cfg(target_pointer_width = "32")]
use sqlite::{sqlite3_bind_blob, sqlite3_bind_text, sqlite3_bind_zeroblob};
const ENCODING_UTF8: c_uchar = SQLITE_UTF8 as c_uchar;
#[cfg_attr(
target_pointer_width = "32",
doc = " - [`&str`](str) (via [`sqlite3_bind_text`])"
)]
#[cfg_attr(
target_pointer_width = "64",
doc = " - [`&str`](str) (via [`sqlite3_bind_text64`])"
)]
#[cfg_attr(
target_pointer_width = "32",
doc = " - [`&[u8]`](primitive@slice) (via [`sqlite3_bind_blob`])"
)]
#[cfg_attr(
target_pointer_width = "64",
doc = " - [`&[u8]`](primitive@slice) (via [`sqlite3_bind_blob64`])"
)]
#[cfg_attr(
target_pointer_width = "32",
doc = " - [`Reservation`] (via [`sqlite3_bind_zeroblob`])"
)]
#[cfg_attr(
target_pointer_width = "64",
doc = " - [`Reservation`] (via [`sqlite3_bind_zeroblob64`])"
)]
pub trait Bind<'b> {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b;
}
macro_rules! bind {
{ $fn:ident($stmt:expr, $index:expr, $($arg:expr),*) } => {
{
let result = unsafe { $fn($stmt.as_ptr(), $index.value(), $($arg),*) };
match Error::<ErrorMessage>::from_connection($stmt, result) {
Some(err) => Err(err),
None => Ok(()),
}
}
};
}
impl<'b> Bind<'b> for i32 {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_int(statement, index, self) }
}
}
impl<'b> Bind<'b> for i64 {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_int64(statement, index, self) }
}
}
impl<'b> Bind<'b> for f64 {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_double(statement, index, self) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [`&str`](str) via [`sqlite3_bind_text`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [`&str`](str) via [`sqlite3_bind_text64`]."
)]
impl<'b> Bind<'b> for &str {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_text(statement, index, self.as_ptr() as *const i8, self.len() as c_int, SQLITE_TRANSIENT) }?;
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_text64(statement, index, self.as_ptr() as *const i8, self.len() as sqlite3_uint64, SQLITE_TRANSIENT, ENCODING_UTF8) }?;
Ok(())
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [`&[u8]`](primitive@slice) via [`sqlite3_bind_blob`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [`&[u8]`](primitive@slice) via [`sqlite3_bind_blob64`]."
)]
impl<'b> Bind<'b> for &[u8] {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_blob(statement, index, self.as_ptr() as *const c_void, self.len() as c_int, SQLITE_TRANSIENT) }
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_blob64(statement, index, self.as_ptr() as *const c_void, self.len() as sqlite3_uint64, SQLITE_TRANSIENT) }
}
}
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
#[doc(alias = "SQLITE_STATIC")]
pub struct Static<'a, T: ?Sized>(&'a T);
impl<'a, T: ?Sized> Static<'a, T> {
pub const fn new(value: &'a T) -> Self {
Self(value)
}
#[inline]
pub(super) fn as_ptr(&self) -> *const T {
self.0 as *const T
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [`&str`](str) via [`sqlite3_bind_text`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [`&str`](str) via [`sqlite3_bind_text64`]."
)]
impl<'b, 'a: 'b> Bind<'b> for Static<'a, str> {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_text(statement, index, self.as_ptr() as *const i8, self.0.len() as c_int, SQLITE_STATIC) }?;
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_text64(statement, index, self.as_ptr() as *const i8, self.0.len() as sqlite3_uint64, SQLITE_STATIC, ENCODING_UTF8) }?;
Ok(())
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [`&[u8]`](primitive@slice) via [`sqlite3_bind_blob`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [`&[u8]`](primitive@slice) via [`sqlite3_bind_blob64`]."
)]
impl<'b, 'a: 'b> Bind<'b> for Static<'a, [u8]> {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_blob(statement, index, self.as_ptr() as *const c_void, self.0.len() as c_int, SQLITE_STATIC) }
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_blob64(statement, index, self.as_ptr() as *const c_void, self.0.len() as sqlite3_uint64, SQLITE_STATIC) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [`String`] via [`sqlite3_bind_text`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [`String`] via [`sqlite3_bind_text64`]."
)]
impl<'b> Bind<'b> for String {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
unsafe { self.as_str().bind(statement, index) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a `Vec<u8>` via [`sqlite3_bind_blob`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a `Vec<u8>` via [`sqlite3_bind_blob64`]."
)]
impl<'b> Bind<'b> for Vec<u8> {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
unsafe { self.as_slice().bind(statement, index) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [blob reservation](Reservation) via [`sqlite3_bind_zeroblob`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [blob reservation](Reservation) via [`sqlite3_bind_zeroblob64`]."
)]
impl<'b> Bind<'b> for Reservation {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_zeroblob(statement, index, self.0 as c_int) }
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_zeroblob64(statement, index, self.len() as sqlite3_uint64) }
}
}
impl<'b, T> Bind<'b> for Option<T>
where
T: Bind<'b>,
{
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
if let Some(value) = self {
unsafe { value.bind(statement, index) }
} else {
unsafe { Null.bind(statement, index) }
}
}
}
pub(crate) struct Null;
impl<'b> Bind<'b> for Null {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: Index) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_null(statement, index,) }
}
}
pub const fn destructor<T>() -> sqlite3_destructor_type {
sqlite3_destructor_type::new(destroy::<T>)
}
unsafe extern "C" fn destroy<T>(p: *mut c_void) {
unsafe { ptr::drop_in_place(p) };
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Index(NonZero<c_int>);
impl Index {
pub const INITIAL: Self = unsafe { Self(NonZero::new_unchecked(1)) };
pub const fn new(value: c_int) -> Result<Self, ()> {
match NonZero::new(value) {
Some(value) => Ok(Self(value)),
None => Err(Error::range()),
}
}
pub const unsafe fn new_unchecked(value: c_int) -> Self {
Self(unsafe { NonZero::new_unchecked(value) })
}
#[inline]
pub const fn value(&self) -> c_int {
self.0.get()
}
pub const fn iter(&self) -> Indexes {
Indexes::new(*self)
}
pub const fn next(&self) -> Self {
unsafe { Self::new_unchecked(self.value() + 1) }
}
}
impl IntoIterator for Index {
type Item = Index;
type IntoIter = Indexes;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl From<Index> for i32 {
fn from(index: Index) -> Self {
index.value() as Self
}
}
impl From<Index> for u32 {
fn from(index: Index) -> Self {
index.value() as Self
}
}
impl From<Index> for i64 {
fn from(index: Index) -> Self {
index.value() as Self
}
}
impl From<Index> for u64 {
fn from(index: Index) -> Self {
index.value() as Self
}
}
impl From<Index> for isize {
fn from(index: Index) -> Self {
index.value() as Self
}
}
impl From<Index> for usize {
fn from(index: Index) -> Self {
index.value() as Self
}
}
impl TryFrom<i32> for Index {
type Error = Error<()>;
fn try_from(value: i32) -> Result<Self, ()> {
Self::new(value as c_int)
}
}
impl TryFrom<i64> for Index {
type Error = Error<()>;
fn try_from(value: i64) -> Result<Self, ()> {
match c_int::try_from(value) {
Ok(value) => Self::new(value),
Err(_) => Err(Error::range()),
}
}
}
impl TryFrom<isize> for Index {
type Error = Error<()>;
fn try_from(value: isize) -> Result<Self, ()> {
Self::new(value as c_int)
}
}
impl TryFrom<u32> for Index {
type Error = Error<()>;
fn try_from(value: u32) -> Result<Self, ()> {
match c_int::try_from(value) {
Ok(value) => Self::new(value),
Err(_) => Err(Error::range()),
}
}
}
impl TryFrom<u64> for Index {
type Error = Error<()>;
fn try_from(value: u64) -> Result<Self, ()> {
match c_int::try_from(value) {
Ok(value) => Self::new(value),
Err(_) => Err(Error::range()),
}
}
}
impl TryFrom<usize> for Index {
type Error = Error<()>;
fn try_from(value: usize) -> Result<Self, ()> {
match c_int::try_from(value) {
Ok(value) => Self::new(value),
Err(_) => Err(Error::range()),
}
}
}
#[cfg(feature = "lang-step-trait")]
impl core::iter::Step for Index {
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
if start.0.get() <= end.0.get() {
let steps = (end.0.get() - start.0.get()) as usize;
(steps, Some(steps))
} else {
(0, None)
}
}
fn forward_checked(start: Self, count: usize) -> Option<Self> {
let count = c_int::try_from(count).ok()?;
let new_value = start.0.get().checked_add(count)?;
NonZero::new(new_value).map(Self)
}
fn backward_checked(start: Self, count: usize) -> Option<Self> {
let count = c_int::try_from(count).ok()?;
let new_value = start.0.get().checked_sub(count)?;
NonZero::new(new_value).map(Self)
}
}
pub struct Indexes {
current: Index,
}
impl Indexes {
const fn new(initial: Index) -> Self {
Self { current: initial }
}
}
impl Iterator for Indexes {
type Item = Index;
fn next(&mut self) -> Option<Self::Item> {
let current = self.current;
self.current = self.current.next();
Some(current)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, None)
}
}