1use crate::api::ffiext::PointerMutFlex;
4use crate::api::types::SqliteType;
5use crate::error::Error;
6use crate::{err, ffi};
7use std::ffi::CStr;
8
9#[derive(Debug)]
11pub struct Row<'stmt> {
12 pub(in crate::api) raw: PointerMutFlex<'stmt, ffi::sqlite3_stmt>,
14}
15impl Row<'_> {
16 #[allow(clippy::missing_panics_doc, reason = "Panic should never occur during normal operation")]
18 pub fn len(&self) -> usize {
19 let columns = unsafe { ffi::sqlite3_data_count(self.raw.as_ptr()) };
20 #[allow(clippy::expect_used, reason = "Panic should never occur during normal operation")]
23 usize::try_from(columns).expect("amount of columns is greater than `usize::MAX`")
24 }
25 #[must_use]
27 pub fn is_empty(&self) -> bool {
28 self.len() == 0
29 }
30
31 pub fn read<T>(&self, column: std::ffi::c_int) -> Result<T, Error>
36 where
37 SqliteType: TryInto<T>,
38 <SqliteType as TryInto<T>>::Error: std::error::Error + Send + 'static,
39 {
40 let type_ = unsafe { ffi::sqlite3_column_type(self.raw.as_ptr(), column) };
42 let value = match type_ {
43 ffi::SQLITE_NULL => SqliteType::Null,
44 ffi::SQLITE_INTEGER => self.read_integer(column)?,
45 ffi::SQLITE_FLOAT => self.read_real(column)?,
46 ffi::SQLITE_TEXT => self.read_text(column)?,
47 ffi::SQLITE_BLOB => self.read_blob(column)?,
48 _ => return Err(err!("Unknown SQLite column type: {type_}")),
49 };
50
51 value.try_into().map_err(|e| err!(with: e, "Failed to load from SQLite type"))
53 }
54 fn read_integer(&self, column: std::ffi::c_int) -> Result<SqliteType, Error> {
56 let value = unsafe { ffi::sqlite3_column_int64(self.raw.as_ptr(), column) };
57 Ok(SqliteType::Integer(value))
58 }
59 fn read_real(&self, column: std::ffi::c_int) -> Result<SqliteType, Error> {
61 let value = unsafe { ffi::sqlite3_column_double(self.raw.as_ptr(), column) };
62 Ok(SqliteType::Real(value))
63 }
64 fn read_text(&self, column: std::ffi::c_int) -> Result<SqliteType, Error> {
66 let chars = unsafe { ffi::sqlite3_column_text(self.raw.as_ptr(), column) };
68 let text = unsafe { CStr::from_ptr(chars as _) };
69
70 let text = text.to_str().map_err(|e| err!(with: e, "SQLite string is not valid UTF-8"))?;
72 Ok(SqliteType::Text(text.to_string()))
73 }
74 fn read_blob(&self, column: std::ffi::c_int) -> Result<SqliteType, Error> {
76 let data = unsafe { ffi::sqlite3_column_blob(self.raw.as_ptr(), column) };
78 let false = data.is_null() else {
79 return Ok(SqliteType::Blob(Vec::new()));
81 };
82
83 let len = unsafe { ffi::sqlite3_column_bytes(self.raw.as_ptr(), column) };
85 let bytes = unsafe { std::slice::from_raw_parts(data as *const u8, len as usize) };
86 Ok(SqliteType::Blob(bytes.to_vec()))
87 }
88}