Skip to main content

rust_ef/provider/
db_value_convert.rs

1//! `TryFrom<DbValue>` impls for primitive and feature-gated types.
2
3use super::db_value::{DbValue, DbValueConvertError};
4
5impl TryFrom<DbValue> for i32 {
6    type Error = DbValueConvertError;
7    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
8        match v {
9            DbValue::I32(n) => Ok(n),
10            DbValue::I16(n) => Ok(n as i32),
11            DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
12                source: DbValue::I64(n),
13                target_type: "i32",
14            }),
15            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
16                source: DbValue::String(s),
17                target_type: "i32",
18            }),
19            other => Err(DbValueConvertError {
20                source: other,
21                target_type: "i32",
22            }),
23        }
24    }
25}
26
27impl TryFrom<DbValue> for i64 {
28    type Error = DbValueConvertError;
29    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
30        match v {
31            DbValue::I64(n) => Ok(n),
32            DbValue::I32(n) => Ok(n as i64),
33            DbValue::I16(n) => Ok(n as i64),
34            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
35                source: DbValue::String(s),
36                target_type: "i64",
37            }),
38            other => Err(DbValueConvertError {
39                source: other,
40                target_type: "i64",
41            }),
42        }
43    }
44}
45
46impl TryFrom<DbValue> for f64 {
47    type Error = DbValueConvertError;
48    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
49        match v {
50            DbValue::F64(x) => Ok(x),
51            DbValue::F32(x) => Ok(x as f64),
52            DbValue::I32(n) => Ok(n as f64),
53            DbValue::I64(n) => Ok(n as f64),
54            DbValue::I16(n) => Ok(n as f64),
55            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
56                source: DbValue::String(s),
57                target_type: "f64",
58            }),
59            other => Err(DbValueConvertError {
60                source: other,
61                target_type: "f64",
62            }),
63        }
64    }
65}
66
67impl TryFrom<DbValue> for f32 {
68    type Error = DbValueConvertError;
69    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
70        match v {
71            DbValue::F32(x) => Ok(x),
72            DbValue::F64(x) => Ok(x as f32),
73            DbValue::I32(n) => Ok(n as f32),
74            DbValue::I64(n) => Ok(n as f32),
75            DbValue::I16(n) => Ok(n as f32),
76            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
77                source: DbValue::String(s),
78                target_type: "f32",
79            }),
80            other => Err(DbValueConvertError {
81                source: other,
82                target_type: "f32",
83            }),
84        }
85    }
86}
87
88impl TryFrom<DbValue> for String {
89    type Error = DbValueConvertError;
90    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
91        match v {
92            DbValue::String(s) => Ok(s),
93            DbValue::Bool(b) => Ok(b.to_string()),
94            DbValue::I16(n) => Ok(n.to_string()),
95            DbValue::I32(n) => Ok(n.to_string()),
96            DbValue::I64(n) => Ok(n.to_string()),
97            DbValue::F32(x) => Ok(x.to_string()),
98            DbValue::F64(x) => Ok(x.to_string()),
99            #[cfg(feature = "chrono")]
100            DbValue::DateTime(dt) => Ok(dt.to_rfc3339()),
101            #[cfg(feature = "chrono")]
102            DbValue::NaiveDateTime(ndt) => Ok(ndt.to_string()),
103            #[cfg(feature = "chrono")]
104            DbValue::NaiveDate(nd) => Ok(nd.to_string()),
105            #[cfg(feature = "uuid")]
106            DbValue::Uuid(u) => Ok(u.to_string()),
107            #[cfg(feature = "decimal")]
108            DbValue::Decimal(d) => Ok(d.to_string()),
109            other => Err(DbValueConvertError {
110                source: other,
111                target_type: "String",
112            }),
113        }
114    }
115}
116
117impl TryFrom<DbValue> for bool {
118    type Error = DbValueConvertError;
119    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
120        match v {
121            DbValue::Bool(b) => Ok(b),
122            DbValue::I64(n) => Ok(n != 0),
123            DbValue::I32(n) => Ok(n != 0),
124            DbValue::I16(n) => Ok(n != 0),
125            DbValue::String(s) => {
126                let lower = s.to_ascii_lowercase();
127                match lower.as_str() {
128                    "true" | "t" | "1" => Ok(true),
129                    "false" | "f" | "0" => Ok(false),
130                    _ => Err(DbValueConvertError {
131                        source: DbValue::String(s),
132                        target_type: "bool",
133                    }),
134                }
135            }
136            other => Err(DbValueConvertError {
137                source: other,
138                target_type: "bool",
139            }),
140        }
141    }
142}
143
144impl TryFrom<DbValue> for Vec<u8> {
145    type Error = DbValueConvertError;
146    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
147        match v {
148            DbValue::Bytes(b) => Ok(b),
149            other => Err(DbValueConvertError {
150                source: other,
151                target_type: "Vec<u8>",
152            }),
153        }
154    }
155}
156
157impl TryFrom<DbValue> for i16 {
158    type Error = DbValueConvertError;
159    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
160        match v {
161            DbValue::I16(n) => Ok(n),
162            DbValue::I32(n) => n.try_into().map_err(|_| DbValueConvertError {
163                source: DbValue::I32(n),
164                target_type: "i16",
165            }),
166            DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
167                source: DbValue::I64(n),
168                target_type: "i16",
169            }),
170            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
171                source: DbValue::String(s),
172                target_type: "i16",
173            }),
174            other => Err(DbValueConvertError {
175                source: other,
176                target_type: "i16",
177            }),
178        }
179    }
180}
181
182impl TryFrom<DbValue> for i8 {
183    type Error = DbValueConvertError;
184    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
185        match v {
186            DbValue::I16(n) => n.try_into().map_err(|_| DbValueConvertError {
187                source: DbValue::I16(n),
188                target_type: "i8",
189            }),
190            DbValue::I32(n) => n.try_into().map_err(|_| DbValueConvertError {
191                source: DbValue::I32(n),
192                target_type: "i8",
193            }),
194            DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
195                source: DbValue::I64(n),
196                target_type: "i8",
197            }),
198            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
199                source: DbValue::String(s),
200                target_type: "i8",
201            }),
202            other => Err(DbValueConvertError {
203                source: other,
204                target_type: "i8",
205            }),
206        }
207    }
208}
209
210impl TryFrom<DbValue> for u32 {
211    type Error = DbValueConvertError;
212    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
213        match v {
214            DbValue::I16(n) => (n as i32).try_into().map_err(|_| DbValueConvertError {
215                source: DbValue::I16(n),
216                target_type: "u32",
217            }),
218            DbValue::I32(n) => n.try_into().map_err(|_| DbValueConvertError {
219                source: DbValue::I32(n),
220                target_type: "u32",
221            }),
222            DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
223                source: DbValue::I64(n),
224                target_type: "u32",
225            }),
226            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
227                source: DbValue::String(s),
228                target_type: "u32",
229            }),
230            other => Err(DbValueConvertError {
231                source: other,
232                target_type: "u32",
233            }),
234        }
235    }
236}
237
238impl TryFrom<DbValue> for u64 {
239    type Error = DbValueConvertError;
240    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
241        match v {
242            DbValue::I16(n) => (n as i64).try_into().map_err(|_| DbValueConvertError {
243                source: DbValue::I16(n),
244                target_type: "u64",
245            }),
246            DbValue::I32(n) => (n as i64).try_into().map_err(|_| DbValueConvertError {
247                source: DbValue::I32(n),
248                target_type: "u64",
249            }),
250            DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
251                source: DbValue::I64(n),
252                target_type: "u64",
253            }),
254            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
255                source: DbValue::String(s),
256                target_type: "u64",
257            }),
258            other => Err(DbValueConvertError {
259                source: other,
260                target_type: "u64",
261            }),
262        }
263    }
264}
265
266// --- Feature-gated TryFrom impls for native chrono / uuid / decimal types ---
267
268#[cfg(feature = "chrono")]
269impl TryFrom<DbValue> for chrono::DateTime<chrono::Utc> {
270    type Error = DbValueConvertError;
271    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
272        match v {
273            DbValue::DateTime(dt) => Ok(dt),
274            DbValue::String(s) => chrono::DateTime::parse_from_rfc3339(&s)
275                .map(|dt| dt.with_timezone(&chrono::Utc))
276                .map_err(|_| DbValueConvertError {
277                    source: DbValue::String(s),
278                    target_type: "DateTime<Utc>",
279                }),
280            other => Err(DbValueConvertError {
281                source: other,
282                target_type: "DateTime<Utc>",
283            }),
284        }
285    }
286}
287
288#[cfg(feature = "chrono")]
289impl TryFrom<DbValue> for chrono::NaiveDateTime {
290    type Error = DbValueConvertError;
291    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
292        match v {
293            DbValue::NaiveDateTime(ndt) => Ok(ndt),
294            DbValue::String(s) => {
295                chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S")
296                    .or_else(|_| {
297                        // Fallback: ISO 8601 / RFC 3339 without timezone
298                        chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S")
299                    })
300                    .map_err(|_| DbValueConvertError {
301                        source: DbValue::String(s),
302                        target_type: "NaiveDateTime",
303                    })
304            }
305            other => Err(DbValueConvertError {
306                source: other,
307                target_type: "NaiveDateTime",
308            }),
309        }
310    }
311}
312
313#[cfg(feature = "chrono")]
314impl TryFrom<DbValue> for chrono::NaiveDate {
315    type Error = DbValueConvertError;
316    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
317        match v {
318            DbValue::NaiveDate(nd) => Ok(nd),
319            DbValue::String(s) => {
320                chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").map_err(|_| DbValueConvertError {
321                    source: DbValue::String(s),
322                    target_type: "NaiveDate",
323                })
324            }
325            other => Err(DbValueConvertError {
326                source: other,
327                target_type: "NaiveDate",
328            }),
329        }
330    }
331}
332
333#[cfg(feature = "uuid")]
334impl TryFrom<DbValue> for uuid::Uuid {
335    type Error = DbValueConvertError;
336    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
337        match v {
338            DbValue::Uuid(u) => Ok(u),
339            DbValue::String(s) => uuid::Uuid::parse_str(&s).map_err(|_| DbValueConvertError {
340                source: DbValue::String(s),
341                target_type: "Uuid",
342            }),
343            other => Err(DbValueConvertError {
344                source: other,
345                target_type: "Uuid",
346            }),
347        }
348    }
349}
350
351#[cfg(feature = "decimal")]
352impl TryFrom<DbValue> for rust_decimal::Decimal {
353    type Error = DbValueConvertError;
354    fn try_from(v: DbValue) -> Result<Self, Self::Error> {
355        match v {
356            DbValue::Decimal(d) => Ok(d),
357            DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
358                source: DbValue::String(s),
359                target_type: "Decimal",
360            }),
361            DbValue::I32(n) => Ok(rust_decimal::Decimal::from(n)),
362            DbValue::I64(n) => Ok(rust_decimal::Decimal::from(n)),
363            other => Err(DbValueConvertError {
364                source: other,
365                target_type: "Decimal",
366            }),
367        }
368    }
369}