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 {
40 match value {
41 ValueRef::Null => JsonValue::Null,
42 ValueRef::Integer(i) => JsonValue::Number(i.into()),
43 ValueRef::Real(f) => serde_json::Number::from_f64(f)
44 .map(JsonValue::Number)
45 .unwrap_or(JsonValue::Null),
46 ValueRef::Text(bytes) => {
47 let s = String::from_utf8_lossy(bytes).to_string();
48 if s.starts_with('{') || s.starts_with('[') {
50 serde_json::from_str(&s).unwrap_or(JsonValue::String(s))
51 } else {
52 JsonValue::String(s)
53 }
54 }
55 ValueRef::Blob(bytes) => {
56 match std::str::from_utf8(bytes) {
58 Ok(s) => JsonValue::String(s.to_string()),
59 Err(_) => {
60 JsonValue::String(base64_encode(bytes))
62 }
63 }
64 }
65 }
66}
67
68pub fn get_value_at_index(row: &rusqlite::Row<'_>, index: usize) -> JsonValue {
70 if let Ok(v) = row.get_ref(index) {
72 from_sqlite_value(v)
73 } else {
74 JsonValue::Null
75 }
76}
77
78fn base64_encode(data: &[u8]) -> String {
80 const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
81
82 let mut result = String::new();
83 let mut i = 0;
84
85 while i < data.len() {
86 let b0 = data[i];
87 let b1 = data.get(i + 1).copied().unwrap_or(0);
88 let b2 = data.get(i + 2).copied().unwrap_or(0);
89
90 result.push(ALPHABET[(b0 >> 2) as usize] as char);
91 result.push(ALPHABET[(((b0 & 0x03) << 4) | (b1 >> 4)) as usize] as char);
92
93 if i + 1 < data.len() {
94 result.push(ALPHABET[(((b1 & 0x0f) << 2) | (b2 >> 6)) as usize] as char);
95 } else {
96 result.push('=');
97 }
98
99 if i + 2 < data.len() {
100 result.push(ALPHABET[(b2 & 0x3f) as usize] as char);
101 } else {
102 result.push('=');
103 }
104
105 i += 3;
106 }
107
108 result
109}
110
111#[derive(Debug, Clone)]
113pub struct JsonColumn(pub JsonValue);
114
115impl FromSql for JsonColumn {
116 fn column_result(value: ValueRef<'_>) -> Result<Self, FromSqlError> {
117 match value {
118 ValueRef::Text(bytes) => {
119 let s = std::str::from_utf8(bytes).map_err(|e| FromSqlError::Other(Box::new(e)))?;
120 let json: JsonValue =
121 serde_json::from_str(s).map_err(|e| FromSqlError::Other(Box::new(e)))?;
122 Ok(JsonColumn(json))
123 }
124 ValueRef::Null => Ok(JsonColumn(JsonValue::Null)),
125 _ => Err(FromSqlError::InvalidType),
126 }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_filter_value_to_sqlite_null() {
136 let result = filter_value_to_sqlite(&FilterValue::Null);
137 assert!(matches!(result, Value::Null));
138 }
139
140 #[test]
141 fn test_filter_value_to_sqlite_bool() {
142 let result = filter_value_to_sqlite(&FilterValue::Bool(true));
143 assert!(matches!(result, Value::Integer(1)));
144
145 let result = filter_value_to_sqlite(&FilterValue::Bool(false));
146 assert!(matches!(result, Value::Integer(0)));
147 }
148
149 #[test]
150 fn test_filter_value_to_sqlite_int() {
151 let result = filter_value_to_sqlite(&FilterValue::Int(42));
152 assert!(matches!(result, Value::Integer(42)));
153 }
154
155 #[test]
156 #[allow(clippy::approx_constant)]
157 fn test_filter_value_to_sqlite_float() {
158 let result = filter_value_to_sqlite(&FilterValue::Float(3.14));
159 match result {
160 Value::Real(f) => assert!((f - 3.14).abs() < f64::EPSILON),
161 _ => panic!("Expected Real"),
162 }
163 }
164
165 #[test]
166 fn test_filter_value_to_sqlite_string() {
167 let result = filter_value_to_sqlite(&FilterValue::String("hello".to_string()));
168 assert!(matches!(result, Value::Text(s) if s == "hello"));
169 }
170
171 #[test]
172 fn test_from_sqlite_value_null() {
173 let result = from_sqlite_value(ValueRef::Null);
174 assert_eq!(result, JsonValue::Null);
175 }
176
177 #[test]
178 fn test_from_sqlite_value_integer() {
179 let result = from_sqlite_value(ValueRef::Integer(42));
180 assert_eq!(result, JsonValue::Number(42.into()));
181 }
182
183 #[test]
184 #[allow(clippy::approx_constant)]
185 fn test_from_sqlite_value_real() {
186 let result = from_sqlite_value(ValueRef::Real(3.14));
187 if let JsonValue::Number(n) = result {
188 assert!((n.as_f64().unwrap() - 3.14).abs() < f64::EPSILON);
189 } else {
190 panic!("Expected Number");
191 }
192 }
193
194 #[test]
195 fn test_from_sqlite_value_text() {
196 let result = from_sqlite_value(ValueRef::Text(b"hello"));
197 assert_eq!(result, JsonValue::String("hello".to_string()));
198 }
199
200 #[test]
201 fn test_from_sqlite_value_json_text() {
202 let result = from_sqlite_value(ValueRef::Text(b"{\"key\": \"value\"}"));
203 if let JsonValue::Object(map) = result {
204 assert_eq!(
205 map.get("key"),
206 Some(&JsonValue::String("value".to_string()))
207 );
208 } else {
209 panic!("Expected Object");
210 }
211 }
212
213 #[test]
214 fn test_base64_encode() {
215 let result = base64_encode(b"Hello");
216 assert_eq!(result, "SGVsbG8=");
217 }
218}