1use crate::config::DatabaseBackend;
4use crate::error::{SqlxError, SqlxResult};
5use serde_json::Value as JsonValue;
6use sqlx::Row;
7
8pub enum SqlxRow {
10 #[cfg(feature = "postgres")]
12 Postgres(sqlx::postgres::PgRow),
13 #[cfg(feature = "mysql")]
15 MySql(sqlx::mysql::MySqlRow),
16 #[cfg(feature = "sqlite")]
18 Sqlite(sqlx::sqlite::SqliteRow),
19}
20
21impl SqlxRow {
22 pub fn backend(&self) -> DatabaseBackend {
24 match self {
25 #[cfg(feature = "postgres")]
26 Self::Postgres(_) => DatabaseBackend::Postgres,
27 #[cfg(feature = "mysql")]
28 Self::MySql(_) => DatabaseBackend::MySql,
29 #[cfg(feature = "sqlite")]
30 Self::Sqlite(_) => DatabaseBackend::Sqlite,
31 }
32 }
33
34 pub fn get<T>(&self, index: usize) -> SqlxResult<T>
36 where
37 T: SqlxDecode,
38 {
39 T::decode_from_row(self, index)
40 }
41
42 pub fn get_by_name<T>(&self, name: &str) -> SqlxResult<T>
44 where
45 T: SqlxDecodeNamed,
46 {
47 T::decode_by_name(self, name)
48 }
49
50 pub fn try_get<T>(&self, index: usize) -> SqlxResult<Option<T>>
52 where
53 T: SqlxDecode,
54 {
55 match T::decode_from_row(self, index) {
56 Ok(v) => Ok(Some(v)),
57 Err(SqlxError::Sqlx(sqlx::Error::ColumnNotFound(_))) => Ok(None),
58 Err(e) => Err(e),
59 }
60 }
61
62 pub fn len(&self) -> usize {
64 match self {
65 #[cfg(feature = "postgres")]
66 Self::Postgres(row) => row.len(),
67 #[cfg(feature = "mysql")]
68 Self::MySql(row) => row.len(),
69 #[cfg(feature = "sqlite")]
70 Self::Sqlite(row) => row.len(),
71 }
72 }
73
74 pub fn is_empty(&self) -> bool {
76 self.len() == 0
77 }
78
79 pub fn to_json(&self) -> SqlxResult<JsonValue> {
81 match self {
82 #[cfg(feature = "postgres")]
83 Self::Postgres(row) => row_to_json_pg(row),
84 #[cfg(feature = "mysql")]
85 Self::MySql(row) => row_to_json_mysql(row),
86 #[cfg(feature = "sqlite")]
87 Self::Sqlite(row) => row_to_json_sqlite(row),
88 }
89 }
90}
91
92pub trait SqlxDecode: Sized {
94 fn decode_from_row(row: &SqlxRow, index: usize) -> SqlxResult<Self>;
96}
97
98pub trait SqlxDecodeNamed: Sized {
100 fn decode_by_name(row: &SqlxRow, name: &str) -> SqlxResult<Self>;
102}
103
104macro_rules! impl_decode {
106 ($ty:ty) => {
107 impl SqlxDecode for $ty {
108 fn decode_from_row(row: &SqlxRow, index: usize) -> SqlxResult<Self> {
109 match row {
110 #[cfg(feature = "postgres")]
111 SqlxRow::Postgres(r) => r.try_get(index).map_err(SqlxError::from),
112 #[cfg(feature = "mysql")]
113 SqlxRow::MySql(r) => r.try_get(index).map_err(SqlxError::from),
114 #[cfg(feature = "sqlite")]
115 SqlxRow::Sqlite(r) => r.try_get(index).map_err(SqlxError::from),
116 }
117 }
118 }
119
120 impl SqlxDecodeNamed for $ty {
121 fn decode_by_name(row: &SqlxRow, name: &str) -> SqlxResult<Self> {
122 match row {
123 #[cfg(feature = "postgres")]
124 SqlxRow::Postgres(r) => r.try_get(name).map_err(SqlxError::from),
125 #[cfg(feature = "mysql")]
126 SqlxRow::MySql(r) => r.try_get(name).map_err(SqlxError::from),
127 #[cfg(feature = "sqlite")]
128 SqlxRow::Sqlite(r) => r.try_get(name).map_err(SqlxError::from),
129 }
130 }
131 }
132 };
133}
134
135impl_decode!(i32);
136impl_decode!(i64);
137impl_decode!(f32);
138impl_decode!(f64);
139impl_decode!(bool);
140impl_decode!(String);
141impl_decode!(Vec<u8>);
142
143#[cfg(feature = "postgres")]
145fn row_to_json_pg(row: &sqlx::postgres::PgRow) -> SqlxResult<JsonValue> {
146 use sqlx::Column;
147 let mut obj = serde_json::Map::new();
148 for (i, col) in row.columns().iter().enumerate() {
149 let name = col.name().to_string();
150 let value: Option<JsonValue> = row.try_get(i).ok();
151 obj.insert(name, value.unwrap_or(JsonValue::Null));
152 }
153 Ok(JsonValue::Object(obj))
154}
155
156#[cfg(feature = "mysql")]
157fn row_to_json_mysql(row: &sqlx::mysql::MySqlRow) -> SqlxResult<JsonValue> {
158 use sqlx::Column;
159 let mut obj = serde_json::Map::new();
160 for (i, col) in row.columns().iter().enumerate() {
161 let name = col.name().to_string();
162 let value: Option<JsonValue> = row.try_get(i).ok();
163 obj.insert(name, value.unwrap_or(JsonValue::Null));
164 }
165 Ok(JsonValue::Object(obj))
166}
167
168#[cfg(feature = "sqlite")]
169fn row_to_json_sqlite(row: &sqlx::sqlite::SqliteRow) -> SqlxResult<JsonValue> {
170 use sqlx::Column;
171 let mut obj = serde_json::Map::new();
172 for (i, col) in row.columns().iter().enumerate() {
173 let name = col.name().to_string();
174 if let Ok(v) = row.try_get::<String, _>(i) {
176 obj.insert(name, JsonValue::String(v));
177 } else if let Ok(v) = row.try_get::<i64, _>(i) {
178 obj.insert(name, JsonValue::Number(v.into()));
179 } else if let Ok(v) = row.try_get::<f64, _>(i) {
180 if let Some(n) = serde_json::Number::from_f64(v) {
181 obj.insert(name, JsonValue::Number(n));
182 } else {
183 obj.insert(name, JsonValue::Null);
184 }
185 } else if let Ok(v) = row.try_get::<bool, _>(i) {
186 obj.insert(name, JsonValue::Bool(v));
187 } else {
188 obj.insert(name, JsonValue::Null);
189 }
190 }
191 Ok(JsonValue::Object(obj))
192}
193
194pub trait FromSqlxRow: Sized {
196 fn from_row(row: SqlxRow) -> SqlxResult<Self>;
198}