reifydb_type/value/
try_from.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the MIT, see license.md file
3
4use std::fmt::{Display, Formatter};
5
6use crate::{
7	Blob, Date, DateTime, Decimal, Duration, IdentityId, Int, OrderedF32, OrderedF64, RowNumber, Time, Type, Uint,
8	Uuid4, Uuid7, Value,
9};
10
11/// Error type for Value extraction failures
12#[derive(Debug, Clone, PartialEq)]
13pub enum FromValueError {
14	/// The Value variant doesn't match the expected type
15	TypeMismatch {
16		expected: Type,
17		found: Type,
18	},
19	/// Numeric value out of range for target type
20	OutOfRange {
21		value: String,
22		target_type: &'static str,
23	},
24}
25
26impl Display for FromValueError {
27	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
28		match self {
29			FromValueError::TypeMismatch {
30				expected,
31				found,
32			} => {
33				write!(f, "type mismatch: expected {:?}, found {:?}", expected, found)
34			}
35			FromValueError::OutOfRange {
36				value,
37				target_type,
38			} => {
39				write!(f, "value {} out of range for type {}", value, target_type)
40			}
41		}
42	}
43}
44
45impl std::error::Error for FromValueError {}
46
47/// Trait for strict extraction of Rust types from Value.
48///
49/// This is the inverse of `IntoValue`. Each implementation only accepts
50/// the exact matching Value variant (e.g., `i64` only accepts `Value::Int8`).
51///
52/// For Undefined values or type mismatches, use `from_value()` which returns
53/// `Option<Self>` for convenience.
54pub trait TryFromValue: Sized {
55	/// Attempt to extract a value of this type from a Value.
56	///
57	/// Returns an error if the Value variant doesn't match the expected type.
58	/// Note: This does NOT handle Undefined - use `from_value()` for that.
59	fn try_from_value(value: &Value) -> Result<Self, FromValueError>;
60
61	/// Extract from Value, returning None for Undefined or type mismatch.
62	///
63	/// This is the recommended method for most use cases as it handles
64	/// Undefined values gracefully.
65	fn from_value(value: &Value) -> Option<Self> {
66		match value {
67			Value::Undefined => None,
68			v => Self::try_from_value(v).ok(),
69		}
70	}
71}
72
73/// Trait for widening extraction of Rust types from Value.
74///
75/// Unlike `TryFromValue`, this allows compatible type conversions:
76/// - `i64` can be extracted from `Int1`, `Int2`, `Int4`, or `Int8`
77/// - `u64` can be extracted from `Uint1`, `Uint2`, `Uint4`, or `Uint8`
78/// - `f64` can be extracted from `Float4`, `Float8`, or any integer type
79pub trait TryFromValueCoerce: Sized {
80	/// Attempt to extract with widening conversion.
81	fn try_from_value_coerce(value: &Value) -> Result<Self, FromValueError>;
82
83	/// Extract with coercion, returning None for Undefined or incompatible types.
84	fn from_value_coerce(value: &Value) -> Option<Self> {
85		match value {
86			Value::Undefined => None,
87			v => Self::try_from_value_coerce(v).ok(),
88		}
89	}
90}
91
92impl TryFromValue for Value {
93	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
94		Ok(value.clone())
95	}
96}
97
98impl TryFromValue for bool {
99	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
100		match value {
101			Value::Boolean(v) => Ok(*v),
102			_ => Err(FromValueError::TypeMismatch {
103				expected: Type::Boolean,
104				found: value.get_type(),
105			}),
106		}
107	}
108}
109
110impl TryFromValue for i8 {
111	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
112		match value {
113			Value::Int1(v) => Ok(*v),
114			_ => Err(FromValueError::TypeMismatch {
115				expected: Type::Int1,
116				found: value.get_type(),
117			}),
118		}
119	}
120}
121
122impl TryFromValue for i16 {
123	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
124		match value {
125			Value::Int2(v) => Ok(*v),
126			_ => Err(FromValueError::TypeMismatch {
127				expected: Type::Int2,
128				found: value.get_type(),
129			}),
130		}
131	}
132}
133
134impl TryFromValue for i32 {
135	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
136		match value {
137			Value::Int4(v) => Ok(*v),
138			_ => Err(FromValueError::TypeMismatch {
139				expected: Type::Int4,
140				found: value.get_type(),
141			}),
142		}
143	}
144}
145
146impl TryFromValue for i64 {
147	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
148		match value {
149			Value::Int8(v) => Ok(*v),
150			_ => Err(FromValueError::TypeMismatch {
151				expected: Type::Int8,
152				found: value.get_type(),
153			}),
154		}
155	}
156}
157
158impl TryFromValue for i128 {
159	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
160		match value {
161			Value::Int16(v) => Ok(*v),
162			_ => Err(FromValueError::TypeMismatch {
163				expected: Type::Int16,
164				found: value.get_type(),
165			}),
166		}
167	}
168}
169
170impl TryFromValue for u8 {
171	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
172		match value {
173			Value::Uint1(v) => Ok(*v),
174			_ => Err(FromValueError::TypeMismatch {
175				expected: Type::Uint1,
176				found: value.get_type(),
177			}),
178		}
179	}
180}
181
182impl TryFromValue for u16 {
183	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
184		match value {
185			Value::Uint2(v) => Ok(*v),
186			_ => Err(FromValueError::TypeMismatch {
187				expected: Type::Uint2,
188				found: value.get_type(),
189			}),
190		}
191	}
192}
193
194impl TryFromValue for u32 {
195	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
196		match value {
197			Value::Uint4(v) => Ok(*v),
198			_ => Err(FromValueError::TypeMismatch {
199				expected: Type::Uint4,
200				found: value.get_type(),
201			}),
202		}
203	}
204}
205
206impl TryFromValue for u64 {
207	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
208		match value {
209			Value::Uint8(v) => Ok(*v),
210			_ => Err(FromValueError::TypeMismatch {
211				expected: Type::Uint8,
212				found: value.get_type(),
213			}),
214		}
215	}
216}
217
218impl TryFromValue for u128 {
219	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
220		match value {
221			Value::Uint16(v) => Ok(*v),
222			_ => Err(FromValueError::TypeMismatch {
223				expected: Type::Uint16,
224				found: value.get_type(),
225			}),
226		}
227	}
228}
229
230impl TryFromValue for f32 {
231	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
232		match value {
233			Value::Float4(v) => Ok(v.value()),
234			_ => Err(FromValueError::TypeMismatch {
235				expected: Type::Float4,
236				found: value.get_type(),
237			}),
238		}
239	}
240}
241
242impl TryFromValue for f64 {
243	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
244		match value {
245			Value::Float8(v) => Ok(v.value()),
246			_ => Err(FromValueError::TypeMismatch {
247				expected: Type::Float8,
248				found: value.get_type(),
249			}),
250		}
251	}
252}
253
254impl TryFromValue for String {
255	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
256		match value {
257			Value::Utf8(v) => Ok(v.clone()),
258			_ => Err(FromValueError::TypeMismatch {
259				expected: Type::Utf8,
260				found: value.get_type(),
261			}),
262		}
263	}
264}
265
266impl TryFromValue for OrderedF32 {
267	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
268		match value {
269			Value::Float4(v) => Ok(*v),
270			_ => Err(FromValueError::TypeMismatch {
271				expected: Type::Float4,
272				found: value.get_type(),
273			}),
274		}
275	}
276}
277
278impl TryFromValue for OrderedF64 {
279	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
280		match value {
281			Value::Float8(v) => Ok(*v),
282			_ => Err(FromValueError::TypeMismatch {
283				expected: Type::Float8,
284				found: value.get_type(),
285			}),
286		}
287	}
288}
289
290impl TryFromValue for Blob {
291	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
292		match value {
293			Value::Blob(v) => Ok(v.clone()),
294			_ => Err(FromValueError::TypeMismatch {
295				expected: Type::Blob,
296				found: value.get_type(),
297			}),
298		}
299	}
300}
301
302impl TryFromValue for Uuid4 {
303	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
304		match value {
305			Value::Uuid4(v) => Ok(v.clone()),
306			_ => Err(FromValueError::TypeMismatch {
307				expected: Type::Uuid4,
308				found: value.get_type(),
309			}),
310		}
311	}
312}
313
314impl TryFromValue for Uuid7 {
315	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
316		match value {
317			Value::Uuid7(v) => Ok(v.clone()),
318			_ => Err(FromValueError::TypeMismatch {
319				expected: Type::Uuid7,
320				found: value.get_type(),
321			}),
322		}
323	}
324}
325
326impl TryFromValue for Date {
327	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
328		match value {
329			Value::Date(v) => Ok(v.clone()),
330			_ => Err(FromValueError::TypeMismatch {
331				expected: Type::Date,
332				found: value.get_type(),
333			}),
334		}
335	}
336}
337
338impl TryFromValue for DateTime {
339	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
340		match value {
341			Value::DateTime(v) => Ok(v.clone()),
342			_ => Err(FromValueError::TypeMismatch {
343				expected: Type::DateTime,
344				found: value.get_type(),
345			}),
346		}
347	}
348}
349
350impl TryFromValue for Time {
351	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
352		match value {
353			Value::Time(v) => Ok(v.clone()),
354			_ => Err(FromValueError::TypeMismatch {
355				expected: Type::Time,
356				found: value.get_type(),
357			}),
358		}
359	}
360}
361
362impl TryFromValue for Duration {
363	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
364		match value {
365			Value::Duration(v) => Ok(v.clone()),
366			_ => Err(FromValueError::TypeMismatch {
367				expected: Type::Duration,
368				found: value.get_type(),
369			}),
370		}
371	}
372}
373
374impl TryFromValue for RowNumber {
375	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
376		match value {
377			Value::RowNumber(v) => Ok(*v),
378			_ => Err(FromValueError::TypeMismatch {
379				expected: Type::RowNumber,
380				found: value.get_type(),
381			}),
382		}
383	}
384}
385
386impl TryFromValue for IdentityId {
387	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
388		match value {
389			Value::IdentityId(v) => Ok(v.clone()),
390			_ => Err(FromValueError::TypeMismatch {
391				expected: Type::IdentityId,
392				found: value.get_type(),
393			}),
394		}
395	}
396}
397
398impl TryFromValue for Int {
399	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
400		match value {
401			Value::Int(v) => Ok(v.clone()),
402			_ => Err(FromValueError::TypeMismatch {
403				expected: Type::Int,
404				found: value.get_type(),
405			}),
406		}
407	}
408}
409
410impl TryFromValue for Uint {
411	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
412		match value {
413			Value::Uint(v) => Ok(v.clone()),
414			_ => Err(FromValueError::TypeMismatch {
415				expected: Type::Uint,
416				found: value.get_type(),
417			}),
418		}
419	}
420}
421
422impl TryFromValue for Decimal {
423	fn try_from_value(value: &Value) -> Result<Self, FromValueError> {
424		match value {
425			Value::Decimal(v) => Ok(v.clone()),
426			_ => Err(FromValueError::TypeMismatch {
427				expected: Type::Decimal,
428				found: value.get_type(),
429			}),
430		}
431	}
432}
433
434impl TryFromValueCoerce for i64 {
435	fn try_from_value_coerce(value: &Value) -> Result<Self, FromValueError> {
436		match value {
437			Value::Int1(v) => Ok(*v as i64),
438			Value::Int2(v) => Ok(*v as i64),
439			Value::Int4(v) => Ok(*v as i64),
440			Value::Int8(v) => Ok(*v),
441			_ => Err(FromValueError::TypeMismatch {
442				expected: Type::Int8,
443				found: value.get_type(),
444			}),
445		}
446	}
447}
448
449impl TryFromValueCoerce for i128 {
450	fn try_from_value_coerce(value: &Value) -> Result<Self, FromValueError> {
451		match value {
452			Value::Int1(v) => Ok(*v as i128),
453			Value::Int2(v) => Ok(*v as i128),
454			Value::Int4(v) => Ok(*v as i128),
455			Value::Int8(v) => Ok(*v as i128),
456			Value::Int16(v) => Ok(*v),
457			_ => Err(FromValueError::TypeMismatch {
458				expected: Type::Int16,
459				found: value.get_type(),
460			}),
461		}
462	}
463}
464
465impl TryFromValueCoerce for u64 {
466	fn try_from_value_coerce(value: &Value) -> Result<Self, FromValueError> {
467		match value {
468			Value::Uint1(v) => Ok(*v as u64),
469			Value::Uint2(v) => Ok(*v as u64),
470			Value::Uint4(v) => Ok(*v as u64),
471			Value::Uint8(v) => Ok(*v),
472			// Also allow signed integers if non-negative
473			Value::Int1(v) if *v >= 0 => Ok(*v as u64),
474			Value::Int2(v) if *v >= 0 => Ok(*v as u64),
475			Value::Int4(v) if *v >= 0 => Ok(*v as u64),
476			Value::Int8(v) if *v >= 0 => Ok(*v as u64),
477			Value::Int1(v) => Err(FromValueError::OutOfRange {
478				value: v.to_string(),
479				target_type: "u64",
480			}),
481			Value::Int2(v) => Err(FromValueError::OutOfRange {
482				value: v.to_string(),
483				target_type: "u64",
484			}),
485			Value::Int4(v) => Err(FromValueError::OutOfRange {
486				value: v.to_string(),
487				target_type: "u64",
488			}),
489			Value::Int8(v) => Err(FromValueError::OutOfRange {
490				value: v.to_string(),
491				target_type: "u64",
492			}),
493			_ => Err(FromValueError::TypeMismatch {
494				expected: Type::Uint8,
495				found: value.get_type(),
496			}),
497		}
498	}
499}
500
501impl TryFromValueCoerce for u128 {
502	fn try_from_value_coerce(value: &Value) -> Result<Self, FromValueError> {
503		match value {
504			Value::Uint1(v) => Ok(*v as u128),
505			Value::Uint2(v) => Ok(*v as u128),
506			Value::Uint4(v) => Ok(*v as u128),
507			Value::Uint8(v) => Ok(*v as u128),
508			Value::Uint16(v) => Ok(*v),
509			_ => Err(FromValueError::TypeMismatch {
510				expected: Type::Uint16,
511				found: value.get_type(),
512			}),
513		}
514	}
515}
516
517impl TryFromValueCoerce for f64 {
518	fn try_from_value_coerce(value: &Value) -> Result<Self, FromValueError> {
519		match value {
520			Value::Float4(v) => Ok(v.value() as f64),
521			Value::Float8(v) => Ok(v.value()),
522			// Allow integer to float conversion (may lose precision for large values)
523			Value::Int1(v) => Ok(*v as f64),
524			Value::Int2(v) => Ok(*v as f64),
525			Value::Int4(v) => Ok(*v as f64),
526			Value::Int8(v) => Ok(*v as f64),
527			Value::Uint1(v) => Ok(*v as f64),
528			Value::Uint2(v) => Ok(*v as f64),
529			Value::Uint4(v) => Ok(*v as f64),
530			Value::Uint8(v) => Ok(*v as f64),
531			_ => Err(FromValueError::TypeMismatch {
532				expected: Type::Float8,
533				found: value.get_type(),
534			}),
535		}
536	}
537}
538
539#[cfg(test)]
540mod tests {
541	use super::*;
542
543	#[test]
544	fn test_try_from_value_primitives() {
545		// Test boolean
546		assert_eq!(bool::try_from_value(&Value::Boolean(true)), Ok(true));
547		assert_eq!(bool::try_from_value(&Value::Boolean(false)), Ok(false));
548		assert!(bool::try_from_value(&Value::Int4(42)).is_err());
549
550		// Test integers
551		assert_eq!(i8::try_from_value(&Value::Int1(42)), Ok(42i8));
552		assert_eq!(i16::try_from_value(&Value::Int2(1234)), Ok(1234i16));
553		assert_eq!(i32::try_from_value(&Value::Int4(123456)), Ok(123456i32));
554		assert_eq!(i64::try_from_value(&Value::Int8(1234567890)), Ok(1234567890i64));
555
556		// Test unsigned integers
557		assert_eq!(u8::try_from_value(&Value::Uint1(42)), Ok(42u8));
558		assert_eq!(u16::try_from_value(&Value::Uint2(1234)), Ok(1234u16));
559		assert_eq!(u32::try_from_value(&Value::Uint4(123456)), Ok(123456u32));
560		assert_eq!(u64::try_from_value(&Value::Uint8(1234567890)), Ok(1234567890u64));
561
562		// Test string
563		assert_eq!(String::try_from_value(&Value::Utf8("hello".to_string())), Ok("hello".to_string()));
564	}
565
566	#[test]
567	fn test_from_value_undefined() {
568		// from_value should return None for Undefined
569		assert_eq!(bool::from_value(&Value::Undefined), None);
570		assert_eq!(i32::from_value(&Value::Undefined), None);
571		assert_eq!(String::from_value(&Value::Undefined), None);
572
573		// from_value should return None for type mismatch
574		assert_eq!(bool::from_value(&Value::Int4(42)), None);
575		assert_eq!(i32::from_value(&Value::Boolean(true)), None);
576	}
577
578	#[test]
579	fn test_try_from_value_coerce_i64() {
580		// Should accept all signed integer types
581		assert_eq!(i64::try_from_value_coerce(&Value::Int1(42)), Ok(42i64));
582		assert_eq!(i64::try_from_value_coerce(&Value::Int2(1234)), Ok(1234i64));
583		assert_eq!(i64::try_from_value_coerce(&Value::Int4(123456)), Ok(123456i64));
584		assert_eq!(i64::try_from_value_coerce(&Value::Int8(1234567890)), Ok(1234567890i64));
585
586		// Should reject other types
587		assert!(i64::try_from_value_coerce(&Value::Uint4(42)).is_err());
588		assert!(i64::try_from_value_coerce(&Value::Boolean(true)).is_err());
589	}
590
591	#[test]
592	fn test_try_from_value_coerce_u64() {
593		// Should accept all unsigned integer types
594		assert_eq!(u64::try_from_value_coerce(&Value::Uint1(42)), Ok(42u64));
595		assert_eq!(u64::try_from_value_coerce(&Value::Uint2(1234)), Ok(1234u64));
596		assert_eq!(u64::try_from_value_coerce(&Value::Uint4(123456)), Ok(123456u64));
597		assert_eq!(u64::try_from_value_coerce(&Value::Uint8(1234567890)), Ok(1234567890u64));
598
599		// Should accept non-negative signed integers
600		assert_eq!(u64::try_from_value_coerce(&Value::Int4(42)), Ok(42u64));
601
602		// Should reject negative signed integers
603		assert!(u64::try_from_value_coerce(&Value::Int4(-42)).is_err());
604	}
605
606	#[test]
607	fn test_try_from_value_coerce_f64() {
608		// Should accept float types
609		let f4 = OrderedF32::try_from(3.14f32).unwrap();
610		let f8 = OrderedF64::try_from(3.14159f64).unwrap();
611		assert!((f64::try_from_value_coerce(&Value::Float4(f4)).unwrap() - 3.14).abs() < 0.01);
612		assert!((f64::try_from_value_coerce(&Value::Float8(f8)).unwrap() - 3.14159).abs() < 0.00001);
613
614		// Should accept integer types
615		assert_eq!(f64::try_from_value_coerce(&Value::Int4(42)), Ok(42.0f64));
616		assert_eq!(f64::try_from_value_coerce(&Value::Uint4(42)), Ok(42.0f64));
617	}
618
619	#[test]
620	fn test_from_value_coerce_undefined() {
621		// from_value_coerce should return None for Undefined
622		assert_eq!(i64::from_value_coerce(&Value::Undefined), None);
623		assert_eq!(u64::from_value_coerce(&Value::Undefined), None);
624		assert_eq!(f64::from_value_coerce(&Value::Undefined), None);
625	}
626}