use core::slice;
use std::ffi::CStr;
use std::str::from_utf8_unchecked;
use libsqlite3_sys::{
sqlite3_column_blob, sqlite3_column_bytes, sqlite3_column_double, sqlite3_column_int,
sqlite3_column_int64, sqlite3_column_text, sqlite3_column_type, SQLITE_BLOB, SQLITE_FLOAT,
SQLITE_INTEGER, SQLITE_NULL, SQLITE_TEXT,
};
use crate::sqlite::statement::Statement;
use crate::sqlite::type_info::SqliteType;
use crate::sqlite::{Sqlite, SqliteTypeInfo};
use crate::value::RawValue;
pub struct SqliteValue<'c> {
pub(super) index: i32,
pub(super) statement: &'c Statement,
}
impl<'c> SqliteValue<'c> {
pub(super) fn is_null(&self) -> bool {
self.r#type().is_none()
}
fn r#type(&self) -> Option<SqliteType> {
let type_code = unsafe {
if let Some(handle) = self.statement.handle() {
sqlite3_column_type(handle, self.index)
} else {
return None;
}
};
match type_code {
SQLITE_INTEGER => Some(SqliteType::Integer),
SQLITE_FLOAT => Some(SqliteType::Float),
SQLITE_TEXT => Some(SqliteType::Text),
SQLITE_BLOB => Some(SqliteType::Blob),
SQLITE_NULL => None,
_ => unreachable!("received unexpected column type: {}", type_code),
}
}
pub(super) fn int(&self) -> i32 {
unsafe {
self.statement
.handle()
.map_or(0, |handle| sqlite3_column_int(handle, self.index))
}
}
pub(super) fn int64(&self) -> i64 {
unsafe {
self.statement
.handle()
.map_or(0, |handle| sqlite3_column_int64(handle, self.index))
}
}
pub(super) fn double(&self) -> f64 {
unsafe {
self.statement
.handle()
.map_or(0.0, |handle| sqlite3_column_double(handle, self.index))
}
}
pub(super) fn text(&self) -> Option<&'c str> {
unsafe {
self.statement.handle().and_then(|handle| {
let ptr = sqlite3_column_text(handle, self.index);
if ptr.is_null() {
None
} else {
Some(from_utf8_unchecked(CStr::from_ptr(ptr as _).to_bytes()))
}
})
}
}
fn bytes(&self) -> usize {
unsafe {
self.statement
.handle()
.map_or(0, |handle| sqlite3_column_bytes(handle, self.index)) as usize
}
}
pub(super) fn blob(&self) -> &'c [u8] {
let ptr = unsafe {
if let Some(handle) = self.statement.handle() {
sqlite3_column_blob(handle, self.index)
} else {
return &[];
}
};
if ptr.is_null() {
return &[];
}
unsafe { slice::from_raw_parts(ptr as *const u8, self.bytes()) }
}
}
impl<'c> RawValue<'c> for SqliteValue<'c> {
type Database = Sqlite;
fn type_info(&self) -> Option<SqliteTypeInfo> {
Some(SqliteTypeInfo {
r#type: self.r#type()?,
affinity: None,
})
}
}