1use crate::pool::Engine;
7use alibi_core::error::AuthResult;
8use chrono::{DateTime, NaiveDateTime, Utc};
9
10#[derive(Clone, Debug, PartialEq)]
12pub enum SqlValue {
13 Bool(Option<bool>),
14 Int(Option<i32>),
15 BigInt(Option<i64>),
16 Float(Option<f32>),
17 Double(Option<f64>),
18 Text(Option<String>),
19 BpChar(Option<String>),
22 Bytes(Option<Vec<u8>>),
23 Json(Option<Box<serde_json::Value>>),
24 Timestamp(Option<DateTime<Utc>>),
26 NaiveTimestamp(Option<NaiveDateTime>),
28 Uuid(Option<uuid::Uuid>),
29}
30
31impl SqlValue {
32 #[must_use]
34 pub const fn is_null(&self) -> bool {
35 matches!(
36 self,
37 Self::Bool(None)
38 | Self::Int(None)
39 | Self::BigInt(None)
40 | Self::Float(None)
41 | Self::Double(None)
42 | Self::Text(None)
43 | Self::BpChar(None)
44 | Self::Bytes(None)
45 | Self::Json(None)
46 | Self::Timestamp(None)
47 | Self::NaiveTimestamp(None)
48 | Self::Uuid(None)
49 )
50 }
51}
52
53macro_rules! sql_value_from {
54 ($($type:ty => $variant:ident),* $(,)?) => {
55 $(
56 impl From<$type> for SqlValue {
57 fn from(value: $type) -> Self {
58 Self::$variant(Some(value))
59 }
60 }
61 impl From<Option<$type>> for SqlValue {
62 fn from(value: Option<$type>) -> Self {
63 Self::$variant(value)
64 }
65 }
66 )*
67 };
68}
69
70sql_value_from!(
71 bool => Bool,
72 i32 => Int,
73 i64 => BigInt,
74 f32 => Float,
75 f64 => Double,
76 String => Text,
77 Vec<u8> => Bytes,
78 DateTime<Utc> => Timestamp,
79 NaiveDateTime => NaiveTimestamp,
80 uuid::Uuid => Uuid,
81);
82
83impl From<&str> for SqlValue {
84 fn from(value: &str) -> Self {
85 Self::Text(Some(value.to_owned()))
86 }
87}
88
89impl From<Option<&str>> for SqlValue {
90 fn from(value: Option<&str>) -> Self {
91 Self::Text(value.map(str::to_owned))
92 }
93}
94
95impl From<&String> for SqlValue {
96 fn from(value: &String) -> Self {
97 Self::Text(Some(value.clone()))
98 }
99}
100
101impl From<serde_json::Value> for SqlValue {
102 fn from(value: serde_json::Value) -> Self {
103 Self::Json(Some(Box::new(value)))
104 }
105}
106
107impl From<Option<serde_json::Value>> for SqlValue {
108 fn from(value: Option<serde_json::Value>) -> Self {
109 Self::Json(value.map(Box::new))
110 }
111}
112
113#[derive(Clone, Copy, Debug, PartialEq, Eq)]
118pub enum ColumnKind {
119 Text,
121 Json,
123 BpChar,
125 Double,
127 Float,
129 Int,
131 BigInt,
133 Boolean,
135 Other,
137 NaiveTimestamp,
139}
140
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
143pub struct ValueTypeError;
144
145impl std::fmt::Display for ValueTypeError {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 f.write_str("value cannot be represented by its model field")
148 }
149}
150
151impl std::error::Error for ValueTypeError {}
152
153pub trait SqlxValue: Sized {
159 const KIND: ColumnKind;
161
162 fn null() -> SqlValue;
164
165 fn into_sql_value(self) -> SqlValue;
166
167 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError>;
171
172 fn prepare(self, _engine: Engine) -> AuthResult<Self> {
180 Ok(self)
181 }
182}
183
184macro_rules! sqlx_value {
185 ($($type:ty => $variant:ident, $kind:ident),* $(,)?) => {
186 $(
187 impl SqlxValue for $type {
188 const KIND: ColumnKind = ColumnKind::$kind;
189 fn null() -> SqlValue {
190 SqlValue::$variant(None)
191 }
192 fn into_sql_value(self) -> SqlValue {
193 SqlValue::$variant(Some(self))
194 }
195 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError> {
196 match value {
197 SqlValue::$variant(Some(value)) => Ok(value),
198 _ => Err(ValueTypeError),
199 }
200 }
201 }
202 )*
203 };
204}
205
206sqlx_value!(
207 bool => Bool, Boolean,
208 i32 => Int, Int,
209 i64 => BigInt, BigInt,
210 f32 => Float, Float,
211 f64 => Double, Double,
212 Vec<u8> => Bytes, Other,
213 DateTime<Utc> => Timestamp, Other,
214 NaiveDateTime => NaiveTimestamp, NaiveTimestamp,
215 uuid::Uuid => Uuid, Other,
216);
217
218impl SqlxValue for serde_json::Value {
219 const KIND: ColumnKind = ColumnKind::Json;
220 fn null() -> SqlValue {
221 SqlValue::Json(None)
222 }
223 fn into_sql_value(self) -> SqlValue {
224 SqlValue::Json(Some(Box::new(self)))
225 }
226 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError> {
227 match value {
228 SqlValue::Json(Some(value)) => Ok(*value),
229 _ => Err(ValueTypeError),
230 }
231 }
232}
233
234impl<T: SqlxValue> SqlxValue for Option<T> {
235 const KIND: ColumnKind = T::KIND;
236 fn null() -> SqlValue {
237 T::null()
238 }
239 fn into_sql_value(self) -> SqlValue {
240 self.map_or_else(T::null, T::into_sql_value)
241 }
242 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError> {
243 if value == T::null()
244 || matches!(
245 (&value, T::null()),
246 (SqlValue::BpChar(None), SqlValue::Text(None))
247 )
248 {
249 Ok(None)
250 } else {
251 T::from_sql_value(value).map(Some)
252 }
253 }
254 fn prepare(self, engine: Engine) -> AuthResult<Self> {
255 self.map(|value| value.prepare(engine)).transpose()
256 }
257}
258
259impl SqlxValue for String {
260 const KIND: ColumnKind = ColumnKind::Text;
261 fn null() -> SqlValue {
262 SqlValue::Text(None)
263 }
264 fn into_sql_value(self) -> SqlValue {
265 SqlValue::Text(Some(self))
266 }
267 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError> {
268 match value {
269 SqlValue::Text(Some(value)) | SqlValue::BpChar(Some(value)) => Ok(value),
270 _ => Err(ValueTypeError),
271 }
272 }
273}