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 Number,
121 Text,
123 Json,
125 BpChar,
127 Double,
129 Float,
131 Int,
133 BigInt,
135 Boolean,
137 Other,
139 NaiveTimestamp,
141}
142
143#[derive(Clone, Copy, Debug, PartialEq, Eq)]
145pub struct ValueTypeError;
146
147impl std::fmt::Display for ValueTypeError {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.write_str("value cannot be represented by its model field")
150 }
151}
152
153impl std::error::Error for ValueTypeError {}
154
155pub trait SqlxValue: Sized {
161 const KIND: ColumnKind;
163
164 fn null() -> SqlValue;
166
167 fn into_sql_value(self) -> SqlValue;
168
169 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError>;
173
174 fn prepare(self, _engine: Engine) -> AuthResult<Self> {
182 Ok(self)
183 }
184}
185
186macro_rules! sqlx_value {
187 ($($type:ty => $variant:ident, $kind:ident),* $(,)?) => {
188 $(
189 impl SqlxValue for $type {
190 const KIND: ColumnKind = ColumnKind::$kind;
191 fn null() -> SqlValue {
192 SqlValue::$variant(None)
193 }
194 fn into_sql_value(self) -> SqlValue {
195 SqlValue::$variant(Some(self))
196 }
197 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError> {
198 match value {
199 SqlValue::$variant(Some(value)) => Ok(value),
200 _ => Err(ValueTypeError),
201 }
202 }
203 }
204 )*
205 };
206}
207
208sqlx_value!(
209 bool => Bool, Boolean,
210 i32 => Int, Int,
211 i64 => BigInt, BigInt,
212 f32 => Float, Float,
213 f64 => Double, Double,
214 Vec<u8> => Bytes, Other,
215 DateTime<Utc> => Timestamp, Other,
216 NaiveDateTime => NaiveTimestamp, NaiveTimestamp,
217 uuid::Uuid => Uuid, Other,
218);
219
220impl SqlxValue for serde_json::Value {
221 const KIND: ColumnKind = ColumnKind::Json;
222 fn null() -> SqlValue {
223 SqlValue::Json(None)
224 }
225 fn into_sql_value(self) -> SqlValue {
226 SqlValue::Json(Some(Box::new(self)))
227 }
228 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError> {
229 match value {
230 SqlValue::Json(Some(value)) => Ok(*value),
231 _ => Err(ValueTypeError),
232 }
233 }
234}
235
236impl<T: SqlxValue> SqlxValue for Option<T> {
237 const KIND: ColumnKind = T::KIND;
238 fn null() -> SqlValue {
239 T::null()
240 }
241 fn into_sql_value(self) -> SqlValue {
242 self.map_or_else(T::null, T::into_sql_value)
243 }
244 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError> {
245 if value == T::null()
246 || matches!(
247 (&value, T::null()),
248 (SqlValue::BpChar(None), SqlValue::Text(None))
249 )
250 {
251 Ok(None)
252 } else {
253 T::from_sql_value(value).map(Some)
254 }
255 }
256 fn prepare(self, engine: Engine) -> AuthResult<Self> {
257 self.map(|value| value.prepare(engine)).transpose()
258 }
259}
260
261impl SqlxValue for String {
262 const KIND: ColumnKind = ColumnKind::Text;
263 fn null() -> SqlValue {
264 SqlValue::Text(None)
265 }
266 fn into_sql_value(self) -> SqlValue {
267 SqlValue::Text(Some(self))
268 }
269 fn from_sql_value(value: SqlValue) -> Result<Self, ValueTypeError> {
270 match value {
271 SqlValue::Text(Some(value)) | SqlValue::BpChar(Some(value)) => Ok(value),
272 _ => Err(ValueTypeError),
273 }
274 }
275}