Skip to main content

reifydb_type/value/
try_from.rs

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