1#[non_exhaustive]
12#[derive(Debug, Clone, PartialEq)]
13pub enum Value {
14 Null,
16 Bool(bool),
18 I64(i64),
20 F64(f64),
22 Text(String),
24 Bytes(Vec<u8>),
26 #[cfg(feature = "json")]
28 Json(serde_json::Value),
29 #[cfg(feature = "uuid")]
31 Uuid(uuid::Uuid),
32 #[cfg(feature = "chrono")]
34 DateTimeUtc(chrono::DateTime<chrono::Utc>),
35 #[cfg(feature = "chrono")]
37 NaiveDateTime(chrono::NaiveDateTime),
38 #[cfg(feature = "chrono")]
40 NaiveDate(chrono::NaiveDate),
41 #[cfg(feature = "chrono")]
43 NaiveTime(chrono::NaiveTime),
44 #[cfg(feature = "decimal")]
48 Decimal(rust_decimal::Decimal),
49}
50
51pub trait IntoBind {
53 fn into_bind(self) -> Value;
55}
56
57macro_rules! impl_into_bind_i64 {
58 ($($t:ty),* $(,)?) => {
59 $(
60 impl IntoBind for $t {
61 fn into_bind(self) -> Value {
62 Value::I64(self as i64)
63 }
64 }
65 )*
66 };
67}
68
69impl_into_bind_i64!(i8, i16, i32, i64, u8, u16, u32);
71
72impl_into_bind_i64!(u64, usize, isize);
84
85impl IntoBind for f32 {
86 fn into_bind(self) -> Value {
87 Value::F64(self as f64)
88 }
89}
90
91impl IntoBind for f64 {
92 fn into_bind(self) -> Value {
93 Value::F64(self)
94 }
95}
96
97impl IntoBind for bool {
98 fn into_bind(self) -> Value {
99 Value::Bool(self)
100 }
101}
102
103impl IntoBind for &str {
104 fn into_bind(self) -> Value {
105 Value::Text(self.to_owned())
106 }
107}
108
109impl IntoBind for String {
110 fn into_bind(self) -> Value {
111 Value::Text(self)
112 }
113}
114
115impl IntoBind for &String {
116 fn into_bind(self) -> Value {
117 Value::Text(self.clone())
118 }
119}
120
121impl IntoBind for Vec<u8> {
122 fn into_bind(self) -> Value {
123 Value::Bytes(self)
124 }
125}
126
127impl IntoBind for &[u8] {
128 fn into_bind(self) -> Value {
129 Value::Bytes(self.to_vec())
130 }
131}
132
133impl IntoBind for Value {
134 fn into_bind(self) -> Value {
135 self
136 }
137}
138
139impl<T: IntoBind> IntoBind for Option<T> {
140 fn into_bind(self) -> Value {
141 match self {
142 None => Value::Null,
143 Some(inner) => inner.into_bind(),
144 }
145 }
146}
147
148#[cfg(feature = "json")]
149impl IntoBind for serde_json::Value {
150 fn into_bind(self) -> Value {
151 Value::Json(self)
152 }
153}
154
155#[cfg(feature = "uuid")]
156impl IntoBind for uuid::Uuid {
157 fn into_bind(self) -> Value {
158 Value::Uuid(self)
159 }
160}
161
162#[cfg(feature = "chrono")]
163impl IntoBind for chrono::DateTime<chrono::Utc> {
164 fn into_bind(self) -> Value {
165 Value::DateTimeUtc(self)
166 }
167}
168
169#[cfg(feature = "chrono")]
170impl IntoBind for chrono::NaiveDateTime {
171 fn into_bind(self) -> Value {
172 Value::NaiveDateTime(self)
173 }
174}
175
176#[cfg(feature = "chrono")]
177impl IntoBind for chrono::NaiveDate {
178 fn into_bind(self) -> Value {
179 Value::NaiveDate(self)
180 }
181}
182
183#[cfg(feature = "chrono")]
184impl IntoBind for chrono::NaiveTime {
185 fn into_bind(self) -> Value {
186 Value::NaiveTime(self)
187 }
188}
189
190#[cfg(feature = "decimal")]
191impl IntoBind for rust_decimal::Decimal {
192 fn into_bind(self) -> Value {
193 Value::Decimal(self)
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn integers_become_i64() {
203 assert_eq!(7i8.into_bind(), Value::I64(7));
204 assert_eq!(7i16.into_bind(), Value::I64(7));
205 assert_eq!(7i32.into_bind(), Value::I64(7));
206 assert_eq!(7i64.into_bind(), Value::I64(7));
207 assert_eq!(7u8.into_bind(), Value::I64(7));
208 assert_eq!(7u16.into_bind(), Value::I64(7));
209 assert_eq!(7u32.into_bind(), Value::I64(7));
210 assert_eq!(7u64.into_bind(), Value::I64(7));
211 assert_eq!(7usize.into_bind(), Value::I64(7));
212 assert_eq!(7isize.into_bind(), Value::I64(7));
213 }
214
215 #[test]
216 fn u64_above_i64_max_wraps_intentionally() {
217 assert_eq!(((i64::MAX as u64) + 1).into_bind(), Value::I64(i64::MIN));
219 assert_eq!(u64::MAX.into_bind(), Value::I64(-1));
220 }
221
222 #[test]
223 fn floats_become_f64() {
224 assert_eq!(1.5f32.into_bind(), Value::F64(1.5));
225 assert_eq!(1.5f64.into_bind(), Value::F64(1.5));
226 }
227
228 #[test]
229 fn bool_becomes_bool() {
230 assert_eq!(true.into_bind(), Value::Bool(true));
231 assert_eq!(false.into_bind(), Value::Bool(false));
232 }
233
234 #[test]
235 fn strings_become_text() {
236 assert_eq!("hi".into_bind(), Value::Text("hi".to_string()));
237 assert_eq!(
238 String::from("hi").into_bind(),
239 Value::Text("hi".to_string())
240 );
241 let owned = String::from("hi");
242 assert_eq!((&owned).into_bind(), Value::Text("hi".to_string()));
243 }
244
245 #[test]
246 fn bytes_become_bytes() {
247 assert_eq!(vec![1u8, 2, 3].into_bind(), Value::Bytes(vec![1, 2, 3]));
248 let slice: &[u8] = &[1, 2, 3];
249 assert_eq!(slice.into_bind(), Value::Bytes(vec![1, 2, 3]));
250 }
251
252 #[test]
253 fn option_none_becomes_null() {
254 assert_eq!(Option::<i64>::None.into_bind(), Value::Null);
255 }
256
257 #[test]
258 fn option_some_becomes_inner() {
259 assert_eq!(Some(5i64).into_bind(), Value::I64(5));
260 }
261
262 #[test]
263 fn value_into_bind_is_identity() {
264 assert_eq!(Value::I64(1).into_bind(), Value::I64(1));
265 }
266}