use super::{ffi, sqlite3_match_version, types::*};
pub use blob::*;
pub use passed_ref::*;
use std::{marker::PhantomData, ptr, slice, str};
pub use unsafe_ptr::*;
pub use value_list::*;
mod blob;
mod passed_ref;
mod test;
mod unsafe_ptr;
mod value_list;
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ValueType {
Integer,
Float,
Text,
Blob,
Null,
}
impl ValueType {
pub(crate) fn from_sqlite(val: i32) -> ValueType {
match val {
ffi::SQLITE_INTEGER => ValueType::Integer,
ffi::SQLITE_FLOAT => ValueType::Float,
ffi::SQLITE_TEXT => ValueType::Text,
ffi::SQLITE_BLOB => ValueType::Blob,
ffi::SQLITE_NULL => ValueType::Null,
_ => unreachable!(),
}
}
}
pub trait FromValue {
fn value_type(&self) -> ValueType;
fn is_null(&self) -> bool {
self.value_type() == ValueType::Null
}
fn get_i32(&self) -> i32;
fn get_i64(&self) -> i64;
fn get_f64(&self) -> f64;
unsafe fn get_blob_unchecked(&self) -> &[u8];
fn get_blob(&mut self) -> Result<&[u8]>;
fn try_get_blob(&self) -> Result<&[u8]> {
match self.value_type() {
ValueType::Blob => Ok(unsafe { self.get_blob_unchecked() }),
_ => Err(SQLITE_MISMATCH),
}
}
unsafe fn get_str_unchecked(&self) -> Result<&str> {
Ok(str::from_utf8(self.get_blob_unchecked())?)
}
fn get_str(&mut self) -> Result<&str> {
Ok(str::from_utf8(self.get_blob()?)?)
}
fn try_get_str(&self) -> Result<&str> {
match self.value_type() {
ValueType::Text => unsafe { self.get_str_unchecked() },
_ => Err(SQLITE_MISMATCH),
}
}
fn to_owned(&self) -> Result<Value> {
match self.value_type() {
ValueType::Integer => Ok(Value::from(self.get_i64())),
ValueType::Float => Ok(Value::from(self.get_f64())),
ValueType::Text => unsafe { Ok(Value::from(self.get_str_unchecked()?.to_owned())) },
ValueType::Blob => unsafe { Ok(Value::from(Blob::from(self.get_blob_unchecked()))) },
ValueType::Null => Ok(Value::Null),
}
}
}
#[repr(transparent)]
pub struct ValueRef {
base: ffi::sqlite3_value,
phantom: PhantomData<*const ffi::sqlite3_value>,
}
impl ValueRef {
#[cfg_attr(not(modern_sqlite), allow(unused))]
pub(crate) unsafe fn from_ptr<'a>(p: *mut ffi::sqlite3_value) -> &'a mut ValueRef {
&mut *(p as *mut ValueRef)
}
pub unsafe fn as_ptr(&self) -> *mut ffi::sqlite3_value {
&self.base as *const ffi::sqlite3_value as _
}
pub fn numeric_type(&mut self) -> ValueType {
unsafe { ValueType::from_sqlite(ffi::sqlite3_value_numeric_type(self.as_ptr())) }
}
pub fn is_from_bind(&self) -> bool {
sqlite3_match_version! {
3_028_000 => unsafe { ffi::sqlite3_value_frombind(self.as_ptr()) != 0 },
_ => false,
}
}
pub fn nochange(&self) -> bool {
sqlite3_match_version! {
3_022_000 => (unsafe { ffi::sqlite3_value_nochange(self.as_ptr()) } != 0),
_ => false,
}
}
unsafe fn get_ref_internal<T: 'static>(&self) -> Option<&PassedRef<T>> {
sqlite3_match_version! {
3_020_000 => (ffi::sqlite3_value_pointer(self.as_ptr(), POINTER_TAG) as *const PassedRef<T>).as_ref(),
_ => None,
}
}
pub fn get_ref<T: 'static>(&self) -> Option<&T> {
unsafe { self.get_ref_internal::<T>() }
.map(|x| PassedRef::get(x))
.unwrap_or(None)
}
}
impl FromValue for ValueRef {
fn value_type(&self) -> ValueType {
unsafe { ValueType::from_sqlite(ffi::sqlite3_value_type(self.as_ptr())) }
}
fn get_i32(&self) -> i32 {
unsafe { ffi::sqlite3_value_int(self.as_ptr()) }
}
fn get_i64(&self) -> i64 {
unsafe { ffi::sqlite3_value_int64(self.as_ptr()) }
}
fn get_f64(&self) -> f64 {
unsafe { ffi::sqlite3_value_double(self.as_ptr()) }
}
unsafe fn get_blob_unchecked(&self) -> &[u8] {
let len = ffi::sqlite3_value_bytes(self.as_ptr());
if len == 0 {
return &[];
}
let data = ffi::sqlite3_value_blob(self.as_ptr());
slice::from_raw_parts(data as _, len as _)
}
fn get_blob(&mut self) -> Result<&[u8]> {
unsafe {
let len = ffi::sqlite3_value_bytes(self.as_ptr());
if len == 0 {
return Ok(&[]);
}
let data = ffi::sqlite3_value_blob(self.as_ptr());
if data.is_null() {
Err(SQLITE_NOMEM)
} else {
Ok(slice::from_raw_parts(data as _, len as _))
}
}
}
}
impl PartialEq for ValueRef {
fn eq(&self, other: &Self) -> bool {
if self.value_type() != other.value_type() {
return false;
}
match self.value_type() {
ValueType::Integer => self.get_i64() == other.get_i64(),
ValueType::Float => self.get_f64() == other.get_f64(),
ValueType::Text | ValueType::Blob => unsafe {
self.get_blob_unchecked() == other.get_blob_unchecked()
},
ValueType::Null => false,
}
}
}
impl std::fmt::Debug for ValueRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self.value_type() {
ValueType::Integer => f.debug_tuple("Integer").field(&self.get_i64()).finish(),
ValueType::Float => f.debug_tuple("Float").field(&self.get_f64()).finish(),
ValueType::Text => f
.debug_tuple("Text")
.field(unsafe { &self.get_str_unchecked() })
.finish(),
ValueType::Blob => f
.debug_tuple("Blob")
.field(unsafe { &self.get_blob_unchecked() })
.finish(),
ValueType::Null => {
if let Some(r) = unsafe { self.get_ref_internal::<()>() } {
f.debug_tuple("Null").field(&r).finish()
} else {
f.debug_tuple("Null").finish()
}
}
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
Integer(i64),
Float(f64),
Text(String),
Blob(Blob),
Null,
}
macro_rules! value_from {
($ty:ty as ($x:ident) => $impl:expr) => {
impl From<$ty> for Value {
fn from($x: $ty) -> Value {
$impl
}
}
};
}
value_from!(i32 as (x) => Value::Integer(x as _));
value_from!(i64 as (x) => Value::Integer(x));
value_from!(f64 as (x) => Value::Float(x));
value_from!(String as (x) => Value::Text(x));
value_from!(Blob as (x) => Value::Blob(x));
value_from!(() as (_x) => Value::Null);