Skip to main content

reifydb_type/value/
try_from.rs

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