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}