Skip to main content

sqlite_provider/
value.rs

1use crate::provider::{RawBytes, ValueType};
2
3/// Owned SQLite value.
4#[derive(Clone, Debug, PartialEq)]
5pub enum Value {
6    /// SQL NULL.
7    Null,
8    /// 64-bit integer.
9    Integer(i64),
10    /// 64-bit floating point.
11    Float(f64),
12    /// UTF-8 text.
13    Text(String),
14    /// Arbitrary bytes.
15    Blob(Vec<u8>),
16}
17
18impl Value {
19    /// Return the SQLite value type tag for this owned value.
20    pub fn value_type(&self) -> ValueType {
21        match self {
22            Value::Null => ValueType::Null,
23            Value::Integer(_) => ValueType::Integer,
24            Value::Float(_) => ValueType::Float,
25            Value::Text(_) => ValueType::Text,
26            Value::Blob(_) => ValueType::Blob,
27        }
28    }
29
30    /// Borrow the integer payload when this is `Value::Integer`.
31    pub fn as_i64(&self) -> Option<i64> {
32        match self {
33            Value::Integer(v) => Some(*v),
34            _ => None,
35        }
36    }
37
38    /// Borrow the float payload when this is `Value::Float`.
39    pub fn as_f64(&self) -> Option<f64> {
40        match self {
41            Value::Float(v) => Some(*v),
42            _ => None,
43        }
44    }
45
46    /// Borrow the text payload when this is `Value::Text`.
47    pub fn as_str(&self) -> Option<&str> {
48        match self {
49            Value::Text(v) => Some(v.as_str()),
50            _ => None,
51        }
52    }
53
54    /// Borrow the blob payload when this is `Value::Blob`.
55    pub fn as_blob(&self) -> Option<&[u8]> {
56        match self {
57            Value::Blob(v) => Some(v.as_slice()),
58            _ => None,
59        }
60    }
61}
62
63impl From<i64> for Value {
64    fn from(value: i64) -> Self {
65        Value::Integer(value)
66    }
67}
68
69impl From<f64> for Value {
70    fn from(value: f64) -> Self {
71        Value::Float(value)
72    }
73}
74
75impl From<String> for Value {
76    fn from(value: String) -> Self {
77        Value::Text(value)
78    }
79}
80
81impl From<&str> for Value {
82    fn from(value: &str) -> Self {
83        Value::Text(value.to_owned())
84    }
85}
86
87impl From<Vec<u8>> for Value {
88    fn from(value: Vec<u8>) -> Self {
89        Value::Blob(value)
90    }
91}
92
93/// Borrowed SQLite value view.
94#[derive(Clone, Copy, Debug, PartialEq)]
95pub enum ValueRef<'a> {
96    /// SQL NULL.
97    Null,
98    /// Borrowed 64-bit integer.
99    Integer(i64),
100    /// Borrowed 64-bit floating point.
101    Float(f64),
102    /// Borrowed UTF-8 text.
103    Text(&'a str),
104    /// Borrowed bytes.
105    Blob(&'a [u8]),
106}
107
108impl<'a> ValueRef<'a> {
109    /// Return the SQLite value type tag for this borrowed value.
110    pub fn value_type(&self) -> ValueType {
111        match self {
112            ValueRef::Null => ValueType::Null,
113            ValueRef::Integer(_) => ValueType::Integer,
114            ValueRef::Float(_) => ValueType::Float,
115            ValueRef::Text(_) => ValueType::Text,
116            ValueRef::Blob(_) => ValueType::Blob,
117        }
118    }
119
120    /// Borrow the integer payload when this is `ValueRef::Integer`.
121    pub fn as_i64(&self) -> Option<i64> {
122        match self {
123            ValueRef::Integer(v) => Some(*v),
124            _ => None,
125        }
126    }
127
128    /// Borrow the float payload when this is `ValueRef::Float`.
129    pub fn as_f64(&self) -> Option<f64> {
130        match self {
131            ValueRef::Float(v) => Some(*v),
132            _ => None,
133        }
134    }
135
136    /// Borrow the text payload when this is `ValueRef::Text`.
137    pub fn as_str(&self) -> Option<&'a str> {
138        match self {
139            ValueRef::Text(v) => Some(*v),
140            _ => None,
141        }
142    }
143
144    /// Borrow the blob payload when this is `ValueRef::Blob`.
145    pub fn as_blob(&self) -> Option<&'a [u8]> {
146        match self {
147            ValueRef::Blob(v) => Some(*v),
148            _ => None,
149        }
150    }
151
152    /// Copy this borrowed value into an owned [`Value`].
153    pub fn to_owned(&self) -> Value {
154        match self {
155            ValueRef::Null => Value::Null,
156            ValueRef::Integer(v) => Value::Integer(*v),
157            ValueRef::Float(v) => Value::Float(*v),
158            ValueRef::Text(v) => Value::Text((*v).to_owned()),
159            ValueRef::Blob(v) => Value::Blob((*v).to_vec()),
160        }
161    }
162
163    /// # Safety
164    /// Caller must ensure `raw` points to valid bytes for the duration of `'a`.
165    ///
166    /// If `raw` is valid UTF-8 this returns `ValueRef::Text`; otherwise it
167    /// falls back to `ValueRef::Blob` to avoid unchecked UTF-8 assumptions.
168    pub unsafe fn from_raw_text(raw: RawBytes) -> ValueRef<'a> {
169        match unsafe { raw.as_str() } {
170            Some(text) => ValueRef::Text(text),
171            None => ValueRef::Blob(unsafe { raw.as_slice() }),
172        }
173    }
174
175    /// # Safety
176    /// Caller must ensure `raw` points to valid bytes for the duration of `'a`.
177    pub unsafe fn from_raw_blob(raw: RawBytes) -> ValueRef<'a> {
178        ValueRef::Blob(unsafe { raw.as_slice() })
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use crate::provider::RawBytes;
185
186    use super::{Value, ValueRef};
187
188    #[test]
189    fn value_ref_to_owned() {
190        let value = ValueRef::Text("hello");
191        assert_eq!(value.to_owned(), Value::Text("hello".to_owned()));
192    }
193
194    #[test]
195    fn value_ref_from_raw_text_falls_back_to_blob_when_invalid_utf8() {
196        let bytes = [0xff_u8, 0xfe_u8];
197        let raw = RawBytes {
198            ptr: bytes.as_ptr(),
199            len: bytes.len(),
200        };
201        let value = unsafe { ValueRef::from_raw_text(raw) };
202        assert_eq!(value, ValueRef::Blob(&bytes));
203    }
204}