supabase_client_core/
value.rs1use 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}