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