sqlite_ll/
writable.rs

1use core::ffi::c_int;
2use core::slice;
3use core::str;
4
5use alloc::string::String;
6use alloc::vec::Vec;
7
8use sqlite3_sys as ffi;
9
10use crate::{Error, Result, Statement};
11
12mod sealed {
13    use alloc::string::String;
14    use alloc::vec::Vec;
15
16    pub trait Sealed {}
17    impl Sealed for String {}
18    impl Sealed for Vec<u8> {}
19    impl<T> Sealed for &mut T where T: ?Sized + Sealed {}
20}
21
22/// Trait governing types which can be written to in-place.
23///
24/// Use with [`Statement::read_into`].
25pub trait Writable
26where
27    Self: self::sealed::Sealed,
28{
29    #[doc(hidden)]
30    fn write(&mut self, stmt: &Statement, index: c_int) -> Result<()>;
31}
32
33impl<T> Writable for &mut T
34where
35    T: ?Sized + Writable,
36{
37    #[inline]
38    fn write(&mut self, stmt: &Statement, index: c_int) -> Result<()> {
39        (**self).write(stmt, index)
40    }
41}
42
43/// [`Writable`] implementation for [`String`] which appends the content of the
44/// column to the current container.
45///
46/// # Examples
47///
48/// ```
49/// use sqlite_ll::{Connection, State};
50///
51/// let c = Connection::memory()?;
52///
53/// c.execute(r##"
54/// CREATE TABLE users (name TEXT);
55/// INSERT INTO users (name) VALUES ('Alice'), ('Bob');
56/// "##)?;
57///
58/// let mut stmt = c.prepare("SELECT name FROM users")?;
59/// let mut name = String::new();
60///
61/// while let State::Row = stmt.step()? {
62///     name.clear();
63///     stmt.read_into(0, &mut name)?;
64///     assert!(matches!(name.as_str(), "Alice" | "Bob"));
65/// }
66/// # Ok::<_, sqlite_ll::Error>(())
67/// ```
68///
69/// Automatic conversion:
70///
71/// ```
72/// use sqlite_ll::{Connection, State};
73///
74/// let c = Connection::memory()?;
75///
76/// c.execute(r##"
77/// CREATE TABLE users (id INTEGER);
78/// INSERT INTO users (id) VALUES (1), (2);
79/// "##)?;
80///
81/// let mut stmt = c.prepare("SELECT id FROM users")?;
82/// let mut name = String::new();
83///
84/// while let State::Row = stmt.step()? {
85///     name.clear();
86///     stmt.read_into(0, &mut name)?;
87///     assert!(matches!(name.as_str(), "1" | "2"));
88/// }
89/// # Ok::<_, sqlite_ll::Error>(())
90/// ```
91impl Writable for String {
92    #[inline]
93    fn write(&mut self, stmt: &Statement, index: c_int) -> Result<()> {
94        unsafe {
95            let len = ffi::sqlite3_column_bytes(stmt.as_ptr(), index);
96
97            let Ok(len) = usize::try_from(len) else {
98                return Err(Error::new(ffi::SQLITE_MISMATCH));
99            };
100
101            if len == 0 {
102                return Ok(());
103            }
104
105            // SAFETY: This is guaranteed to return valid UTF-8 by sqlite.
106            let ptr = ffi::sqlite3_column_text(stmt.as_ptr(), index);
107
108            if ptr.is_null() {
109                return Ok(());
110            }
111
112            self.reserve(len);
113
114            let bytes = slice::from_raw_parts(ptr, len);
115            let string = str::from_utf8_unchecked(bytes);
116            self.push_str(string);
117            Ok(())
118        }
119    }
120}
121
122/// [`Writable`] implementation for [`String`] which appends the content of the
123/// column to the current container.
124///
125/// # Examples
126///
127/// ```
128/// use sqlite_ll::{Connection, State};
129///
130/// let c = Connection::memory()?;
131///
132/// c.execute(r##"
133/// CREATE TABLE users (name TEXT);
134/// INSERT INTO users (name) VALUES ('Alice'), ('Bob');
135/// "##)?;
136///
137/// let mut stmt = c.prepare("SELECT name FROM users")?;
138/// let mut name = Vec::<u8>::new();
139///
140/// while let State::Row = stmt.step()? {
141///     name.clear();
142///     stmt.read_into(0, &mut name)?;
143///     assert!(matches!(name.as_slice(), b"Alice" | b"Bob"));
144/// }
145/// # Ok::<_, sqlite_ll::Error>(())
146/// ```
147///
148/// Automatic conversion:
149///
150/// ```
151/// use sqlite_ll::{Connection, State};
152///
153/// let c = Connection::memory()?;
154///
155/// c.execute(r##"
156/// CREATE TABLE users (id INTEGER);
157/// INSERT INTO users (id) VALUES (1), (2);
158/// "##)?;
159///
160/// let mut stmt = c.prepare("SELECT id FROM users")?;
161/// let mut name = Vec::<u8>::new();
162///
163/// while let State::Row = stmt.step()? {
164///     name.clear();
165///     stmt.read_into(0, &mut name)?;
166///     assert!(matches!(name.as_slice(), b"1" | b"2"));
167/// }
168/// # Ok::<_, sqlite_ll::Error>(())
169/// ```
170impl Writable for Vec<u8> {
171    #[inline]
172    fn write(&mut self, stmt: &Statement, index: c_int) -> Result<()> {
173        unsafe {
174            let i = c_int::try_from(index).unwrap_or(c_int::MAX);
175
176            let Ok(len) = usize::try_from(ffi::sqlite3_column_bytes(stmt.as_ptr(), i)) else {
177                return Err(Error::new(ffi::SQLITE_MISMATCH));
178            };
179
180            if len == 0 {
181                return Ok(());
182            }
183
184            let ptr = ffi::sqlite3_column_blob(stmt.as_ptr(), i);
185
186            if ptr.is_null() {
187                return Ok(());
188            }
189
190            self.reserve(len);
191
192            let bytes = slice::from_raw_parts(ptr.cast(), len);
193            self.extend_from_slice(bytes);
194            Ok(())
195        }
196    }
197}