Skip to main content

reifydb_type/value/
try_from.rs

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