1use serde::{Deserialize, Serialize};
2use serde_json::Value as JsonValue;
3#[cfg(feature = "direct-sql")]
4use sqlx::Row as SqlxRow;
5use std::collections::HashMap;
6use std::ops::{Deref, DerefMut};
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
11pub struct Row(pub HashMap<String, JsonValue>);
12
13impl Row {
14 pub fn new() -> Self {
16 Self(HashMap::new())
17 }
18
19 pub fn with_capacity(capacity: usize) -> Self {
21 Self(HashMap::with_capacity(capacity))
22 }
23
24 pub fn set(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) -> &mut Self {
26 self.0.insert(key.into(), value.into());
27 self
28 }
29
30 pub fn get_value(&self, key: &str) -> Option<&JsonValue> {
32 self.0.get(key)
33 }
34
35 pub fn contains(&self, key: &str) -> bool {
37 self.0.contains_key(key)
38 }
39
40 pub fn get_as<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
42 self.0
43 .get(key)
44 .and_then(|v| serde_json::from_value(v.clone()).ok())
45 }
46
47 pub fn columns(&self) -> Vec<&str> {
49 self.0.keys().map(|k| k.as_str()).collect()
50 }
51
52 pub fn len(&self) -> usize {
54 self.0.len()
55 }
56
57 pub fn is_empty(&self) -> bool {
59 self.0.is_empty()
60 }
61
62 pub fn into_inner(self) -> HashMap<String, JsonValue> {
64 self.0
65 }
66}
67
68impl Deref for Row {
69 type Target = HashMap<String, JsonValue>;
70 fn deref(&self) -> &Self::Target {
71 &self.0
72 }
73}
74
75impl DerefMut for Row {
76 fn deref_mut(&mut self) -> &mut Self::Target {
77 &mut self.0
78 }
79}
80
81impl<K: Into<String>, V: Into<JsonValue>> FromIterator<(K, V)> for Row {
82 fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
83 let map = iter
84 .into_iter()
85 .map(|(k, v)| (k.into(), v.into()))
86 .collect();
87 Self(map)
88 }
89}
90
91impl<K: Into<String>, V: Into<JsonValue>, const N: usize> From<[(K, V); N]> for Row {
92 fn from(arr: [(K, V); N]) -> Self {
93 arr.into_iter().collect()
94 }
95}
96
97#[cfg(feature = "direct-sql")]
98impl<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow> for Row {
99 fn from_row(row: &'r sqlx::postgres::PgRow) -> Result<Self, sqlx::Error> {
100 use sqlx::Column;
101 let mut map = Row::new();
102 for col in row.columns() {
103 let name = col.name();
104 if let Ok(v) = row.try_get::<JsonValue, _>(name) {
106 map.set(name, v);
107 } else if let Ok(v) = row.try_get::<String, _>(name) {
108 map.set(name, JsonValue::String(v));
109 } else if let Ok(v) = row.try_get::<i64, _>(name) {
110 map.set(name, JsonValue::Number(v.into()));
111 } else if let Ok(v) = row.try_get::<i32, _>(name) {
112 map.set(name, JsonValue::Number(v.into()));
113 } else if let Ok(v) = row.try_get::<f64, _>(name) {
114 if let Some(n) = serde_json::Number::from_f64(v) {
115 map.set(name, JsonValue::Number(n));
116 } else {
117 map.set(name, JsonValue::Null);
118 }
119 } else if let Ok(v) = row.try_get::<bool, _>(name) {
120 map.set(name, JsonValue::Bool(v));
121 } else if let Ok(v) = row.try_get::<uuid::Uuid, _>(name) {
122 map.set(name, JsonValue::String(v.to_string()));
123 } else if let Ok(v) = row.try_get::<chrono::NaiveDateTime, _>(name) {
124 map.set(name, JsonValue::String(v.to_string()));
125 } else if let Ok(v) = row.try_get::<chrono::DateTime<chrono::Utc>, _>(name) {
126 map.set(name, JsonValue::String(v.to_rfc3339()));
127 } else {
128 map.set(name, JsonValue::Null);
129 }
130 }
131 Ok(map)
132 }
133}
134
135#[macro_export]
143macro_rules! row {
144 () => {
145 $crate::Row::new()
146 };
147 ($(($key:expr, $val:expr)),+ $(,)?) => {{
148 let mut row = $crate::Row::new();
149 $(
150 row.set($key, serde_json::json!($val));
151 )+
152 row
153 }};
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_row_new() {
162 let row = Row::new();
163 assert!(row.is_empty());
164 }
165
166 #[test]
167 fn test_row_set_get() {
168 let mut row = Row::new();
169 row.set("name", JsonValue::String("Auckland".to_string()));
170 assert_eq!(
171 row.get_value("name"),
172 Some(&JsonValue::String("Auckland".to_string()))
173 );
174 assert!(row.contains("name"));
175 assert!(!row.contains("missing"));
176 }
177
178 #[test]
179 fn test_row_macro() {
180 let row = row![("name", "Auckland"), ("id", 1)];
181 assert_eq!(row.len(), 2);
182 assert!(row.contains("name"));
183 assert!(row.contains("id"));
184 }
185
186 #[test]
187 fn test_row_get_as() {
188 let row = row![("count", 42)];
189 assert_eq!(row.get_as::<i64>("count"), Some(42));
190 assert_eq!(row.get_as::<String>("count"), None);
191 }
192
193 #[test]
194 fn test_get_as_wrong_type_returns_none() {
195 let row = row![("flag", true)];
196 assert_eq!(row.get_as::<i64>("flag"), None);
197 assert_eq!(row.get_as::<Vec<String>>("flag"), None);
198 }
199
200 #[test]
201 fn test_columns_returns_all_column_names() {
202 let row = row![("a", 1), ("b", 2), ("c", 3)];
203 let mut cols = row.columns();
204 cols.sort();
205 assert_eq!(cols, vec!["a", "b", "c"]);
206 }
207
208 #[test]
209 fn test_is_empty_on_empty_row() {
210 let row = Row::new();
211 assert!(row.is_empty());
212 }
213
214 #[test]
215 fn test_is_empty_on_non_empty_row() {
216 let row = row![("key", "value")];
217 assert!(!row.is_empty());
218 }
219
220 #[test]
221 fn test_len_counts_columns() {
222 let row = row![("x", 1), ("y", 2)];
223 assert_eq!(row.len(), 2);
224 }
225
226 #[test]
227 fn test_len_empty_row() {
228 let row = Row::new();
229 assert_eq!(row.len(), 0);
230 }
231
232 #[test]
233 fn test_get_value_missing_key() {
234 let row = Row::new();
235 assert_eq!(row.get_value("nonexistent"), None);
236 }
237
238 #[test]
239 fn test_with_capacity_creates_row() {
240 let mut row = Row::with_capacity(10);
241 assert!(row.is_empty());
242 row.set("key", "val");
243 assert_eq!(row.len(), 1);
244 }
245
246 #[test]
247 fn test_into_inner_returns_hashmap() {
248 let row = row![("name", "Alice"), ("age", 30)];
249 let map = row.into_inner();
250 assert_eq!(map.len(), 2);
251 assert_eq!(map.get("name"), Some(&serde_json::json!("Alice")));
252 assert_eq!(map.get("age"), Some(&serde_json::json!(30)));
253 }
254
255 #[test]
256 fn test_deref_access() {
257 let row = row![("city", "Auckland")];
258 assert!(row.contains_key("city"));
260 assert!(!row.contains_key("country"));
261 assert_eq!(row.get("city"), Some(&serde_json::json!("Auckland")));
262 }
263
264 #[test]
265 fn test_from_array_conversion() {
266 let row = Row::from([
267 ("name".to_string(), JsonValue::String("Bob".to_string())),
268 ("score".to_string(), JsonValue::from(100)),
269 ]);
270 assert_eq!(row.len(), 2);
271 assert_eq!(
272 row.get_value("name"),
273 Some(&JsonValue::String("Bob".to_string()))
274 );
275 assert_eq!(row.get_value("score"), Some(&JsonValue::from(100)));
276 }
277
278 #[test]
279 fn test_from_iterator_conversion() {
280 let pairs = vec![
281 ("alpha".to_string(), JsonValue::from(1)),
282 ("beta".to_string(), JsonValue::from(2)),
283 ("gamma".to_string(), JsonValue::from(3)),
284 ];
285 let row: Row = pairs.into_iter().collect();
286 assert_eq!(row.len(), 3);
287 assert_eq!(row.get_as::<i64>("alpha"), Some(1));
288 assert_eq!(row.get_as::<i64>("beta"), Some(2));
289 assert_eq!(row.get_as::<i64>("gamma"), Some(3));
290 }
291
292 #[test]
293 fn test_deref_mut_access() {
294 let mut row = Row::new();
295 row.insert("direct".to_string(), JsonValue::Bool(true));
297 assert_eq!(row.get_value("direct"), Some(&JsonValue::Bool(true)));
298 }
299
300 #[test]
301 fn test_row_serialize_deserialize() {
302 let row = row![("key", "value"), ("num", 42)];
303 let json = serde_json::to_string(&row).unwrap();
304 let deserialized: Row = serde_json::from_str(&json).unwrap();
305 assert_eq!(row, deserialized);
306 }
307}