1use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
6use std::fmt;
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum SqlValue {
11 Null,
13 Integer(i64),
15 Real(f64),
17 Text(String),
19 Blob(Vec<u8>),
21}
22
23impl SqlValue {
24 pub fn is_null(&self) -> bool {
26 matches!(self, Self::Null)
27 }
28
29 pub fn as_i64(&self) -> Option<i64> {
31 match self {
32 Self::Integer(v) => Some(*v),
33 Self::Real(v) => Some(*v as i64),
34 _ => None,
35 }
36 }
37
38 pub fn as_f64(&self) -> Option<f64> {
40 match self {
41 Self::Real(v) => Some(*v),
42 Self::Integer(v) => Some(*v as f64),
43 _ => None,
44 }
45 }
46
47 pub fn as_str(&self) -> Option<&str> {
49 match self {
50 Self::Text(s) => Some(s),
51 _ => None,
52 }
53 }
54
55 pub fn as_bytes(&self) -> Option<&[u8]> {
57 match self {
58 Self::Blob(b) => Some(b),
59 Self::Text(s) => Some(s.as_bytes()),
60 _ => None,
61 }
62 }
63
64 pub fn as_bool(&self) -> Option<bool> {
66 match self {
67 Self::Integer(v) => Some(*v != 0),
68 _ => None,
69 }
70 }
71}
72
73impl fmt::Display for SqlValue {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 match self {
76 Self::Null => write!(f, "NULL"),
77 Self::Integer(v) => write!(f, "{}", v),
78 Self::Real(v) => write!(f, "{}", v),
79 Self::Text(s) => write!(f, "'{}'", s),
80 Self::Blob(b) => write!(f, "BLOB({} bytes)", b.len()),
81 }
82 }
83}
84
85impl FromSql for SqlValue {
86 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
87 Ok(match value {
88 ValueRef::Null => Self::Null,
89 ValueRef::Integer(i) => Self::Integer(i),
90 ValueRef::Real(r) => Self::Real(r),
91 ValueRef::Text(t) => Self::Text(
92 std::str::from_utf8(t)
93 .map_err(|e| FromSqlError::Other(Box::new(e)))?
94 .to_string(),
95 ),
96 ValueRef::Blob(b) => Self::Blob(b.to_vec()),
97 })
98 }
99}
100
101impl ToSql for SqlValue {
102 fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
103 Ok(match self {
104 Self::Null => ToSqlOutput::Owned(rusqlite::types::Value::Null),
105 Self::Integer(i) => ToSqlOutput::Owned(rusqlite::types::Value::Integer(*i)),
106 Self::Real(r) => ToSqlOutput::Owned(rusqlite::types::Value::Real(*r)),
107 Self::Text(s) => ToSqlOutput::Owned(rusqlite::types::Value::Text(s.clone())),
108 Self::Blob(b) => ToSqlOutput::Owned(rusqlite::types::Value::Blob(b.clone())),
109 })
110 }
111}
112
113#[derive(Debug, Clone)]
117pub enum Param {
118 Null,
120 Bool(bool),
122 Int(i64),
124 Uint(u64),
126 Float(f64),
128 Text(String),
130 Blob(Vec<u8>),
132}
133
134impl Param {
135 pub fn null() -> Self {
137 Self::Null
138 }
139}
140
141impl From<bool> for Param {
143 fn from(v: bool) -> Self {
144 Self::Bool(v)
145 }
146}
147
148impl From<i32> for Param {
149 fn from(v: i32) -> Self {
150 Self::Int(v as i64)
151 }
152}
153
154impl From<i64> for Param {
155 fn from(v: i64) -> Self {
156 Self::Int(v)
157 }
158}
159
160impl From<u32> for Param {
161 fn from(v: u32) -> Self {
162 Self::Uint(v as u64)
163 }
164}
165
166impl From<u64> for Param {
167 fn from(v: u64) -> Self {
168 Self::Uint(v)
169 }
170}
171
172impl From<usize> for Param {
173 fn from(v: usize) -> Self {
174 Self::Uint(v as u64)
175 }
176}
177
178impl From<f32> for Param {
179 fn from(v: f32) -> Self {
180 Self::Float(v as f64)
181 }
182}
183
184impl From<f64> for Param {
185 fn from(v: f64) -> Self {
186 Self::Float(v)
187 }
188}
189
190impl From<&str> for Param {
191 fn from(v: &str) -> Self {
192 Self::Text(v.to_string())
193 }
194}
195
196impl From<String> for Param {
197 fn from(v: String) -> Self {
198 Self::Text(v)
199 }
200}
201
202impl From<&String> for Param {
203 fn from(v: &String) -> Self {
204 Self::Text(v.clone())
205 }
206}
207
208impl From<Vec<u8>> for Param {
209 fn from(v: Vec<u8>) -> Self {
210 Self::Blob(v)
211 }
212}
213
214impl From<&[u8]> for Param {
215 fn from(v: &[u8]) -> Self {
216 Self::Blob(v.to_vec())
217 }
218}
219
220impl<T> From<Option<T>> for Param
221where
222 T: Into<Param>,
223{
224 fn from(v: Option<T>) -> Self {
225 match v {
226 Some(inner) => inner.into(),
227 None => Self::Null,
228 }
229 }
230}
231
232impl ToSql for Param {
233 fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
234 Ok(match self {
235 Self::Null => ToSqlOutput::Owned(rusqlite::types::Value::Null),
236 Self::Bool(b) => ToSqlOutput::Owned(rusqlite::types::Value::Integer(if *b { 1 } else { 0 })),
237 Self::Int(i) => ToSqlOutput::Owned(rusqlite::types::Value::Integer(*i)),
238 Self::Uint(u) => ToSqlOutput::Owned(rusqlite::types::Value::Integer(*u as i64)),
239 Self::Float(f) => ToSqlOutput::Owned(rusqlite::types::Value::Real(*f)),
240 Self::Text(s) => ToSqlOutput::Owned(rusqlite::types::Value::Text(s.clone())),
241 Self::Blob(b) => ToSqlOutput::Owned(rusqlite::types::Value::Blob(b.clone())),
242 })
243 }
244}
245
246#[derive(Debug, Clone, Default)]
250pub struct Row {
251 columns: Vec<String>,
253 values: Vec<SqlValue>,
255}
256
257impl Row {
258 pub fn new() -> Self {
260 Self::default()
261 }
262
263 pub fn push(&mut self, name: impl Into<String>, value: SqlValue) {
265 self.columns.push(name.into());
266 self.values.push(value);
267 }
268
269 pub fn len(&self) -> usize {
271 self.columns.len()
272 }
273
274 pub fn is_empty(&self) -> bool {
276 self.columns.is_empty()
277 }
278
279 pub fn columns(&self) -> &[String] {
281 &self.columns
282 }
283
284 pub fn get(&self, index: usize) -> Option<&SqlValue> {
286 self.values.get(index)
287 }
288
289 pub fn get_by_name(&self, name: &str) -> Option<&SqlValue> {
291 self.columns
292 .iter()
293 .position(|c| c == name)
294 .and_then(|i| self.values.get(i))
295 }
296
297 pub fn get_i64(&self, name: &str) -> Option<i64> {
299 self.get_by_name(name).and_then(|v| v.as_i64())
300 }
301
302 pub fn get_f64(&self, name: &str) -> Option<f64> {
304 self.get_by_name(name).and_then(|v| v.as_f64())
305 }
306
307 pub fn get_str(&self, name: &str) -> Option<&str> {
309 self.get_by_name(name).and_then(|v| v.as_str())
310 }
311
312 pub fn get_string(&self, name: &str) -> Option<String> {
314 self.get_str(name).map(|s| s.to_string())
315 }
316
317 pub fn get_bool(&self, name: &str) -> Option<bool> {
319 self.get_by_name(name).and_then(|v| v.as_bool())
320 }
321
322 pub fn get_bytes(&self, name: &str) -> Option<&[u8]> {
324 self.get_by_name(name).and_then(|v| v.as_bytes())
325 }
326}
327
328pub type Rows = Vec<Row>;
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_sql_value() {
337 let v = SqlValue::Integer(42);
338 assert_eq!(v.as_i64(), Some(42));
339 assert_eq!(v.as_f64(), Some(42.0));
340
341 let v = SqlValue::Text("hello".into());
342 assert_eq!(v.as_str(), Some("hello"));
343
344 let v = SqlValue::Null;
345 assert!(v.is_null());
346 }
347
348 #[test]
349 fn test_param_conversions() {
350 let p: Param = 42i32.into();
351 assert!(matches!(p, Param::Int(42)));
352
353 let p: Param = "hello".into();
354 assert!(matches!(p, Param::Text(_)));
355
356 let p: Param = None::<i32>.into();
357 assert!(matches!(p, Param::Null));
358 }
359
360 #[test]
361 fn test_row() {
362 let mut row = Row::new();
363 row.push("id", SqlValue::Integer(1));
364 row.push("name", SqlValue::Text("Alice".into()));
365
366 assert_eq!(row.len(), 2);
367 assert_eq!(row.get_i64("id"), Some(1));
368 assert_eq!(row.get_str("name"), Some("Alice"));
369 assert_eq!(row.get_str("unknown"), None);
370 }
371}