1#[derive(Debug, Clone, PartialEq)]
24pub enum DbValue {
25 Null,
27 Bool(bool),
29 Int(i64),
31 Float(f64),
33 Text(String),
35 Bytes(Vec<u8>),
37
38 #[cfg(feature = "postgres-native")]
45 Date(sqlx::types::chrono::NaiveDate),
46 #[cfg(feature = "postgres-native")]
48 DateTime(sqlx::types::chrono::NaiveDateTime),
49 #[cfg(feature = "postgres-native")]
51 TimestampTz(sqlx::types::chrono::DateTime<sqlx::types::chrono::Utc>),
52 #[cfg(feature = "postgres-native")]
54 Json(sqlx::types::JsonValue),
55 #[cfg(feature = "postgres-native")]
57 Uuid(sqlx::types::Uuid),
58 #[cfg(feature = "postgres-native")]
60 Time(sqlx::types::chrono::NaiveTime),
61 #[cfg(feature = "postgres-native")]
63 TextArray(Vec<String>),
64 #[cfg(feature = "postgres-native")]
66 FloatArray(Vec<f64>),
67 #[cfg(feature = "postgres-native")]
69 OptFloatArray(Vec<Option<f64>>),
70}
71
72#[cfg(feature = "postgres-native")]
73impl From<sqlx::types::chrono::NaiveDate> for DbValue {
74 fn from(v: sqlx::types::chrono::NaiveDate) -> Self {
75 DbValue::Date(v)
76 }
77}
78
79#[cfg(feature = "postgres-native")]
80impl From<sqlx::types::chrono::NaiveDateTime> for DbValue {
81 fn from(v: sqlx::types::chrono::NaiveDateTime) -> Self {
82 DbValue::DateTime(v)
83 }
84}
85
86#[cfg(feature = "postgres-native")]
87impl From<sqlx::types::chrono::DateTime<sqlx::types::chrono::Utc>> for DbValue {
88 fn from(v: sqlx::types::chrono::DateTime<sqlx::types::chrono::Utc>) -> Self {
89 DbValue::TimestampTz(v)
90 }
91}
92
93#[cfg(feature = "postgres-native")]
94impl From<sqlx::types::JsonValue> for DbValue {
95 fn from(v: sqlx::types::JsonValue) -> Self {
96 DbValue::Json(v)
97 }
98}
99
100#[cfg(feature = "postgres-native")]
101impl From<sqlx::types::Uuid> for DbValue {
102 fn from(v: sqlx::types::Uuid) -> Self {
103 DbValue::Uuid(v)
104 }
105}
106
107#[cfg(feature = "postgres-native")]
108impl From<sqlx::types::chrono::NaiveTime> for DbValue {
109 fn from(v: sqlx::types::chrono::NaiveTime) -> Self {
110 DbValue::Time(v)
111 }
112}
113
114#[cfg(feature = "postgres-native")]
115impl From<Vec<String>> for DbValue {
116 fn from(v: Vec<String>) -> Self {
117 DbValue::TextArray(v)
118 }
119}
120
121#[cfg(feature = "postgres-native")]
122impl From<Vec<f64>> for DbValue {
123 fn from(v: Vec<f64>) -> Self {
124 DbValue::FloatArray(v)
125 }
126}
127
128#[cfg(feature = "postgres-native")]
129impl From<Vec<Option<f64>>> for DbValue {
130 fn from(v: Vec<Option<f64>>) -> Self {
131 DbValue::OptFloatArray(v)
132 }
133}
134
135impl From<bool> for DbValue {
136 fn from(v: bool) -> Self {
137 DbValue::Bool(v)
138 }
139}
140
141macro_rules! impl_from_int {
142 ($($t:ty),*) => {
143 $(
144 impl From<$t> for DbValue {
145 fn from(v: $t) -> Self {
146 DbValue::Int(v as i64)
147 }
148 }
149 )*
150 };
151}
152impl_from_int!(i8, i16, i32, i64, u8, u16, u32);
153
154impl From<f32> for DbValue {
155 fn from(v: f32) -> Self {
156 DbValue::Float(v as f64)
157 }
158}
159
160impl From<f64> for DbValue {
161 fn from(v: f64) -> Self {
162 DbValue::Float(v)
163 }
164}
165
166impl From<&str> for DbValue {
167 fn from(v: &str) -> Self {
168 DbValue::Text(v.to_string())
169 }
170}
171
172impl From<String> for DbValue {
173 fn from(v: String) -> Self {
174 DbValue::Text(v)
175 }
176}
177
178impl From<Vec<u8>> for DbValue {
179 fn from(v: Vec<u8>) -> Self {
180 DbValue::Bytes(v)
181 }
182}
183
184impl From<&[u8]> for DbValue {
185 fn from(v: &[u8]) -> Self {
186 DbValue::Bytes(v.to_vec())
187 }
188}
189
190impl<T: Into<DbValue>> From<Option<T>> for DbValue {
192 fn from(v: Option<T>) -> Self {
193 match v {
194 Some(x) => x.into(),
195 None => DbValue::Null,
196 }
197 }
198}
199
200#[cfg(feature = "postgres-native")]
205pub(crate) fn pg_text_array_literal(items: &[String]) -> String {
206 let inner = items
207 .iter()
208 .map(|s| format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")))
209 .collect::<Vec<_>>()
210 .join(",");
211 format!("{{{inner}}}")
212}
213
214#[cfg(feature = "postgres-native")]
217pub(crate) fn pg_float_array_literal<I: IntoIterator<Item = Option<f64>>>(items: I) -> String {
218 let inner = items
219 .into_iter()
220 .map(|o| o.map(|x| x.to_string()).unwrap_or_else(|| "NULL".to_string()))
221 .collect::<Vec<_>>()
222 .join(",");
223 format!("{{{inner}}}")
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn integers_widen_to_int() {
232 assert_eq!(DbValue::from(7i32), DbValue::Int(7));
233 assert_eq!(DbValue::from(7u8), DbValue::Int(7));
234 assert_eq!(DbValue::from(-3i64), DbValue::Int(-3));
235 }
236
237 #[test]
238 fn text_from_str_and_string() {
239 assert_eq!(DbValue::from("hi"), DbValue::Text("hi".into()));
240 assert_eq!(DbValue::from(String::from("hi")), DbValue::Text("hi".into()));
241 }
242
243 #[test]
244 fn option_maps_none_to_null() {
245 let some: DbValue = Some(5i32).into();
246 let none: DbValue = None::<i32>.into();
247 assert_eq!(some, DbValue::Int(5));
248 assert_eq!(none, DbValue::Null);
249 }
250
251 #[test]
252 fn bytes_conversions() {
253 let v: DbValue = vec![1u8, 2, 3].into();
254 assert_eq!(v, DbValue::Bytes(vec![1, 2, 3]));
255 let s: DbValue = [4u8, 5].as_slice().into();
256 assert_eq!(s, DbValue::Bytes(vec![4, 5]));
257 }
258}