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