unistore_sqlite/
types.rs

1//! SQLite 类型映射
2//!
3//! 职责:定义 Rust 与 SQLite 之间的类型转换
4
5use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
6use std::fmt;
7
8/// SQLite 值类型
9#[derive(Debug, Clone, PartialEq)]
10pub enum SqlValue {
11    /// NULL 值
12    Null,
13    /// 整数
14    Integer(i64),
15    /// 浮点数
16    Real(f64),
17    /// 文本
18    Text(String),
19    /// 二进制数据
20    Blob(Vec<u8>),
21}
22
23impl SqlValue {
24    /// 判断是否为 NULL
25    pub fn is_null(&self) -> bool {
26        matches!(self, Self::Null)
27    }
28
29    /// 尝试转换为 i64
30    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    /// 尝试转换为 f64
39    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    /// 尝试转换为字符串引用
48    pub fn as_str(&self) -> Option<&str> {
49        match self {
50            Self::Text(s) => Some(s),
51            _ => None,
52        }
53    }
54
55    /// 尝试转换为字节切片
56    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    /// 尝试转换为 bool
65    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/// 查询参数
114///
115/// 支持多种 Rust 类型到 SQLite 参数的转换
116#[derive(Debug, Clone)]
117pub enum Param {
118    /// NULL
119    Null,
120    /// 布尔值
121    Bool(bool),
122    /// 整数
123    Int(i64),
124    /// 无符号整数
125    Uint(u64),
126    /// 浮点数
127    Float(f64),
128    /// 字符串
129    Text(String),
130    /// 二进制
131    Blob(Vec<u8>),
132}
133
134impl Param {
135    /// 创建 NULL 参数
136    pub fn null() -> Self {
137        Self::Null
138    }
139}
140
141// 实现各种类型到 Param 的转换
142impl 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/// 查询结果行
247///
248/// 包含列名到值的映射
249#[derive(Debug, Clone, Default)]
250pub struct Row {
251    /// 列名列表(保持顺序)
252    columns: Vec<String>,
253    /// 值列表
254    values: Vec<SqlValue>,
255}
256
257impl Row {
258    /// 创建新行
259    pub fn new() -> Self {
260        Self::default()
261    }
262
263    /// 添加列值
264    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    /// 获取列数
270    pub fn len(&self) -> usize {
271        self.columns.len()
272    }
273
274    /// 判断是否为空
275    pub fn is_empty(&self) -> bool {
276        self.columns.is_empty()
277    }
278
279    /// 获取列名列表
280    pub fn columns(&self) -> &[String] {
281        &self.columns
282    }
283
284    /// 按索引获取值
285    pub fn get(&self, index: usize) -> Option<&SqlValue> {
286        self.values.get(index)
287    }
288
289    /// 按列名获取值
290    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    /// 尝试获取 i64 值
298    pub fn get_i64(&self, name: &str) -> Option<i64> {
299        self.get_by_name(name).and_then(|v| v.as_i64())
300    }
301
302    /// 尝试获取 f64 值
303    pub fn get_f64(&self, name: &str) -> Option<f64> {
304        self.get_by_name(name).and_then(|v| v.as_f64())
305    }
306
307    /// 尝试获取字符串值
308    pub fn get_str(&self, name: &str) -> Option<&str> {
309        self.get_by_name(name).and_then(|v| v.as_str())
310    }
311
312    /// 尝试获取字符串值(返回 String)
313    pub fn get_string(&self, name: &str) -> Option<String> {
314        self.get_str(name).map(|s| s.to_string())
315    }
316
317    /// 尝试获取 bool 值
318    pub fn get_bool(&self, name: &str) -> Option<bool> {
319        self.get_by_name(name).and_then(|v| v.as_bool())
320    }
321
322    /// 尝试获取 bytes 值
323    pub fn get_bytes(&self, name: &str) -> Option<&[u8]> {
324        self.get_by_name(name).and_then(|v| v.as_bytes())
325    }
326}
327
328/// 多行结果集
329pub 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}