1use rusqlite::types::{FromSql, FromSqlError, Value, ValueRef};
4use serde_json::Value as JsonValue;
5
6use prax_query::filter::FilterValue;
7
8pub fn filter_value_to_sqlite(value: &FilterValue) -> Value {
10 match value {
11 FilterValue::Null => Value::Null,
12 FilterValue::Bool(b) => Value::Integer(i64::from(*b)),
13 FilterValue::Int(i) => Value::Integer(*i),
14 FilterValue::Float(f) => Value::Real(*f),
15 FilterValue::String(s) => Value::Text(s.clone()),
16 FilterValue::Json(j) => Value::Text(j.to_string()),
17 FilterValue::List(list) => {
18 let json_array: Vec<JsonValue> = list
20 .iter()
21 .map(|v| match v {
22 FilterValue::Null => JsonValue::Null,
23 FilterValue::Bool(b) => JsonValue::Bool(*b),
24 FilterValue::Int(i) => JsonValue::Number((*i).into()),
25 FilterValue::Float(f) => serde_json::Number::from_f64(*f)
26 .map(JsonValue::Number)
27 .unwrap_or(JsonValue::Null),
28 FilterValue::String(s) => JsonValue::String(s.clone()),
29 FilterValue::Json(j) => j.clone(),
30 FilterValue::List(_) => JsonValue::Null, })
32 .collect();
33 Value::Text(serde_json::to_string(&json_array).unwrap_or_default())
34 }
35 }
36}
37
38pub fn from_sqlite_value(value: ValueRef<'_>) -> JsonValue {
49 match value {
50 ValueRef::Null => JsonValue::Null,
51 ValueRef::Integer(i) => JsonValue::Number(i.into()),
52 ValueRef::Real(f) => serde_json::Number::from_f64(f)
53 .map(JsonValue::Number)
54 .unwrap_or(JsonValue::Null),
55 ValueRef::Text(bytes) => {
56 let s = String::from_utf8_lossy(bytes).to_string();
57 if s.starts_with('{') || s.starts_with('[') {
59 serde_json::from_str(&s).unwrap_or(JsonValue::String(s))
60 } else {
61 JsonValue::String(s)
62 }
63 }
64 ValueRef::Blob(bytes) => {
65 match std::str::from_utf8(bytes) {
67 Ok(s) => JsonValue::String(s.to_string()),
68 Err(_) => {
69 JsonValue::String(base64_encode(bytes))
71 }
72 }
73 }
74 }
75}
76
77pub fn get_value_at_index(row: &rusqlite::Row<'_>, index: usize) -> JsonValue {
88 if let Ok(v) = row.get_ref(index) {
90 from_sqlite_value(v)
91 } else {
92 JsonValue::Null
93 }
94}
95
96fn base64_encode(data: &[u8]) -> String {
98 const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
99
100 let mut result = String::new();
101 let mut i = 0;
102
103 while i < data.len() {
104 let b0 = data[i];
105 let b1 = data.get(i + 1).copied().unwrap_or(0);
106 let b2 = data.get(i + 2).copied().unwrap_or(0);
107
108 result.push(ALPHABET[(b0 >> 2) as usize] as char);
109 result.push(ALPHABET[(((b0 & 0x03) << 4) | (b1 >> 4)) as usize] as char);
110
111 if i + 1 < data.len() {
112 result.push(ALPHABET[(((b1 & 0x0f) << 2) | (b2 >> 6)) as usize] as char);
113 } else {
114 result.push('=');
115 }
116
117 if i + 2 < data.len() {
118 result.push(ALPHABET[(b2 & 0x3f) as usize] as char);
119 } else {
120 result.push('=');
121 }
122
123 i += 3;
124 }
125
126 result
127}
128
129#[derive(Debug, Clone)]
131pub struct JsonColumn(pub JsonValue);
132
133impl FromSql for JsonColumn {
134 fn column_result(value: ValueRef<'_>) -> Result<Self, FromSqlError> {
135 match value {
136 ValueRef::Text(bytes) => {
137 let s = std::str::from_utf8(bytes).map_err(|e| FromSqlError::Other(Box::new(e)))?;
138 let json: JsonValue =
139 serde_json::from_str(s).map_err(|e| FromSqlError::Other(Box::new(e)))?;
140 Ok(JsonColumn(json))
141 }
142 ValueRef::Null => Ok(JsonColumn(JsonValue::Null)),
143 _ => Err(FromSqlError::InvalidType),
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_filter_value_to_sqlite_null() {
154 let result = filter_value_to_sqlite(&FilterValue::Null);
155 assert!(matches!(result, Value::Null));
156 }
157
158 #[test]
159 fn test_filter_value_to_sqlite_bool() {
160 let result = filter_value_to_sqlite(&FilterValue::Bool(true));
161 assert!(matches!(result, Value::Integer(1)));
162
163 let result = filter_value_to_sqlite(&FilterValue::Bool(false));
164 assert!(matches!(result, Value::Integer(0)));
165 }
166
167 #[test]
168 fn test_filter_value_to_sqlite_int() {
169 let result = filter_value_to_sqlite(&FilterValue::Int(42));
170 assert!(matches!(result, Value::Integer(42)));
171 }
172
173 #[test]
174 #[allow(clippy::approx_constant)]
175 fn test_filter_value_to_sqlite_float() {
176 let result = filter_value_to_sqlite(&FilterValue::Float(3.14));
177 match result {
178 Value::Real(f) => assert!((f - 3.14).abs() < f64::EPSILON),
179 _ => panic!("Expected Real"),
180 }
181 }
182
183 #[test]
184 fn test_filter_value_to_sqlite_string() {
185 let result = filter_value_to_sqlite(&FilterValue::String("hello".to_string()));
186 assert!(matches!(result, Value::Text(s) if s == "hello"));
187 }
188
189 #[test]
190 fn test_from_sqlite_value_null() {
191 let result = from_sqlite_value(ValueRef::Null);
192 assert_eq!(result, JsonValue::Null);
193 }
194
195 #[test]
196 fn test_from_sqlite_value_integer() {
197 let result = from_sqlite_value(ValueRef::Integer(42));
198 assert_eq!(result, JsonValue::Number(42.into()));
199 }
200
201 #[test]
202 #[allow(clippy::approx_constant)]
203 fn test_from_sqlite_value_real() {
204 let result = from_sqlite_value(ValueRef::Real(3.14));
205 if let JsonValue::Number(n) = result {
206 assert!((n.as_f64().unwrap() - 3.14).abs() < f64::EPSILON);
207 } else {
208 panic!("Expected Number");
209 }
210 }
211
212 #[test]
213 fn test_from_sqlite_value_text() {
214 let result = from_sqlite_value(ValueRef::Text(b"hello"));
215 assert_eq!(result, JsonValue::String("hello".to_string()));
216 }
217
218 #[test]
219 fn test_from_sqlite_value_json_text() {
220 let result = from_sqlite_value(ValueRef::Text(b"{\"key\": \"value\"}"));
221 if let JsonValue::Object(map) = result {
222 assert_eq!(
223 map.get("key"),
224 Some(&JsonValue::String("value".to_string()))
225 );
226 } else {
227 panic!("Expected Object");
228 }
229 }
230
231 #[test]
232 fn test_base64_encode() {
233 let result = base64_encode(b"Hello");
234 assert_eq!(result, "SGVsbG8=");
235 }
236}