Skip to main content

reifydb_type/value/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::{
5	cmp::Ordering,
6	fmt::{Display, Formatter},
7};
8
9use num_traits::ToPrimitive;
10use serde::{Deserialize, Serialize};
11pub mod as_string;
12pub mod blob;
13pub mod boolean;
14pub mod constraint;
15pub mod container;
16pub mod date;
17pub mod datetime;
18pub mod decimal;
19pub mod dictionary;
20pub mod duration;
21pub mod frame;
22pub mod identity;
23pub mod int;
24pub mod into;
25pub mod is;
26pub mod json;
27pub mod number;
28pub mod ordered_f32;
29pub mod ordered_f64;
30pub mod row_number;
31pub mod sumtype;
32pub mod temporal;
33pub mod time;
34pub mod try_from;
35pub mod r#type;
36pub mod uint;
37pub mod uuid;
38
39use std::{fmt, hash, mem};
40
41use blob::Blob;
42use date::Date;
43use datetime::DateTime;
44use decimal::Decimal;
45use dictionary::DictionaryEntryId;
46use duration::Duration;
47use identity::IdentityId;
48use int::Int;
49use ordered_f32::OrderedF32;
50use ordered_f64::OrderedF64;
51use time::Time;
52use r#type::Type;
53use uint::Uint;
54use uuid::{Uuid4, Uuid7};
55
56/// A RQL value, represented as a native Rust type.
57#[derive(Clone, Debug, Serialize, Deserialize)]
58pub enum Value {
59	/// Value is none (think null in common programming languages)
60	None {
61		#[serde(skip, default = "default_none_inner")]
62		inner: Type,
63	},
64	/// A boolean: true or false.
65	Boolean(bool),
66	/// A 4-byte floating point
67	Float4(OrderedF32),
68	/// An 8-byte floating point
69	Float8(OrderedF64),
70	/// A 1-byte signed integer
71	Int1(i8),
72	/// A 2-byte signed integer
73	Int2(i16),
74	/// A 4-byte signed integer
75	Int4(i32),
76	/// An 8-byte signed integer
77	Int8(i64),
78	/// A 16-byte signed integer
79	Int16(i128),
80	/// A UTF-8 encoded text. Maximum 255 bytes
81	Utf8(String),
82	/// A 1-byte unsigned integer
83	Uint1(u8),
84	/// A 2-byte unsigned integer
85	Uint2(u16),
86	/// A 4-byte unsigned integer
87	Uint4(u32),
88	/// A 8-byte unsigned integer
89	Uint8(u64),
90	/// A 16-byte unsigned integer
91	Uint16(u128),
92	/// A date value (year, month, day)
93	Date(Date),
94	/// A date and time value with nanosecond precision in SVTC
95	DateTime(DateTime),
96	/// A time value (hour, minute, second, nanosecond)
97	Time(Time),
98	/// A duration representing a duration
99	Duration(Duration),
100	/// An identity identifier (UUID v7)
101	IdentityId(IdentityId),
102	/// A UUID version 4 (random)
103	Uuid4(Uuid4),
104	/// A UUID version 7 (timestamp-based)
105	Uuid7(Uuid7),
106	/// A binary large object (BLOB)
107	Blob(Blob),
108	/// An arbitrary-precision signed integer
109	Int(Int),
110	/// An arbitrary-precision unsigned integer
111	Uint(Uint),
112	/// An arbitrary-precision decimal
113	Decimal(Decimal),
114	/// A container that can hold any value type
115	Any(Box<Value>),
116	/// A dictionary entry identifier
117	DictionaryId(DictionaryEntryId),
118	/// A type value (first-class type identifier)
119	Type(Type),
120	/// An ordered list of values
121	List(Vec<Value>),
122	/// A record (named fields with values)
123	Record(Vec<(String, Value)>),
124	/// A tuple of heterogeneous values
125	Tuple(Vec<Value>),
126}
127
128fn default_none_inner() -> Type {
129	Type::Any
130}
131
132impl Value {
133	pub fn none() -> Self {
134		Value::None {
135			inner: Type::Any,
136		}
137	}
138
139	pub fn none_of(ty: Type) -> Self {
140		Value::None {
141			inner: ty,
142		}
143	}
144
145	pub fn bool(v: impl Into<bool>) -> Self {
146		Value::Boolean(v.into())
147	}
148
149	pub fn float4(v: impl Into<f32>) -> Self {
150		OrderedF32::try_from(v.into()).map(Value::Float4).unwrap_or(Value::None {
151			inner: Type::Float4,
152		})
153	}
154
155	pub fn float8(v: impl Into<f64>) -> Self {
156		OrderedF64::try_from(v.into()).map(Value::Float8).unwrap_or(Value::None {
157			inner: Type::Float8,
158		})
159	}
160
161	pub fn int1(v: impl Into<i8>) -> Self {
162		Value::Int1(v.into())
163	}
164
165	pub fn int2(v: impl Into<i16>) -> Self {
166		Value::Int2(v.into())
167	}
168
169	pub fn int4(v: impl Into<i32>) -> Self {
170		Value::Int4(v.into())
171	}
172
173	pub fn int8(v: impl Into<i64>) -> Self {
174		Value::Int8(v.into())
175	}
176
177	pub fn int16(v: impl Into<i128>) -> Self {
178		Value::Int16(v.into())
179	}
180
181	pub fn utf8(v: impl Into<String>) -> Self {
182		Value::Utf8(v.into())
183	}
184
185	pub fn uint1(v: impl Into<u8>) -> Self {
186		Value::Uint1(v.into())
187	}
188
189	pub fn uint2(v: impl Into<u16>) -> Self {
190		Value::Uint2(v.into())
191	}
192
193	pub fn uint4(v: impl Into<u32>) -> Self {
194		Value::Uint4(v.into())
195	}
196
197	pub fn uint8(v: impl Into<u64>) -> Self {
198		Value::Uint8(v.into())
199	}
200
201	pub fn uint16(v: impl Into<u128>) -> Self {
202		Value::Uint16(v.into())
203	}
204
205	pub fn date(v: impl Into<Date>) -> Self {
206		Value::Date(v.into())
207	}
208
209	pub fn datetime(v: impl Into<DateTime>) -> Self {
210		Value::DateTime(v.into())
211	}
212
213	pub fn time(v: impl Into<Time>) -> Self {
214		Value::Time(v.into())
215	}
216
217	pub fn duration(v: impl Into<Duration>) -> Self {
218		Value::Duration(v.into())
219	}
220
221	pub fn identity_id(v: impl Into<IdentityId>) -> Self {
222		Value::IdentityId(v.into())
223	}
224
225	pub fn uuid4(v: impl Into<Uuid4>) -> Self {
226		Value::Uuid4(v.into())
227	}
228
229	pub fn uuid7(v: impl Into<Uuid7>) -> Self {
230		Value::Uuid7(v.into())
231	}
232
233	pub fn blob(v: impl Into<Blob>) -> Self {
234		Value::Blob(v.into())
235	}
236
237	pub fn any(v: impl Into<Value>) -> Self {
238		Value::Any(Box::new(v.into()))
239	}
240
241	pub fn list(items: Vec<Value>) -> Self {
242		Value::List(items)
243	}
244
245	pub fn record(fields: Vec<(String, Value)>) -> Self {
246		Value::Record(fields)
247	}
248
249	pub fn to_usize(&self) -> Option<usize> {
250		match self {
251			Value::Uint1(v) => Some(*v as usize),
252			Value::Uint2(v) => Some(*v as usize),
253			Value::Uint4(v) => Some(*v as usize),
254			Value::Uint8(v) => usize::try_from(*v).ok(),
255			Value::Uint16(v) => usize::try_from(*v).ok(),
256			Value::Int1(v) => usize::try_from(*v).ok(),
257			Value::Int2(v) => usize::try_from(*v).ok(),
258			Value::Int4(v) => usize::try_from(*v).ok(),
259			Value::Int8(v) => usize::try_from(*v).ok(),
260			Value::Int16(v) => usize::try_from(*v).ok(),
261			Value::Float4(v) => {
262				let f = v.value();
263				if f >= 0.0 {
264					Some(f as usize)
265				} else {
266					None
267				}
268			}
269			Value::Float8(v) => {
270				let f = v.value();
271				if f >= 0.0 {
272					Some(f as usize)
273				} else {
274					None
275				}
276			}
277			Value::Int(v) => v.0.to_u64().and_then(|n| usize::try_from(n).ok()),
278			Value::Uint(v) => v.0.to_u64().and_then(|n| usize::try_from(n).ok()),
279			Value::Decimal(v) => v.0.to_u64().and_then(|n| usize::try_from(n).ok()),
280			Value::Utf8(s) => {
281				let s = s.trim();
282				if let Ok(n) = s.parse::<u64>() {
283					usize::try_from(n).ok()
284				} else if let Ok(f) = s.parse::<f64>() {
285					if f >= 0.0 {
286						Some(f as usize)
287					} else {
288						None
289					}
290				} else {
291					None
292				}
293			}
294			_ => None,
295		}
296	}
297}
298
299impl PartialEq for Value {
300	fn eq(&self, other: &Self) -> bool {
301		match (self, other) {
302			(
303				Value::None {
304					..
305				},
306				Value::None {
307					..
308				},
309			) => true,
310			(Value::Boolean(l), Value::Boolean(r)) => l == r,
311			(Value::Float4(l), Value::Float4(r)) => l == r,
312			(Value::Float8(l), Value::Float8(r)) => l == r,
313			(Value::Int1(l), Value::Int1(r)) => l == r,
314			(Value::Int2(l), Value::Int2(r)) => l == r,
315			(Value::Int4(l), Value::Int4(r)) => l == r,
316			(Value::Int8(l), Value::Int8(r)) => l == r,
317			(Value::Int16(l), Value::Int16(r)) => l == r,
318			(Value::Utf8(l), Value::Utf8(r)) => l == r,
319			(Value::Uint1(l), Value::Uint1(r)) => l == r,
320			(Value::Uint2(l), Value::Uint2(r)) => l == r,
321			(Value::Uint4(l), Value::Uint4(r)) => l == r,
322			(Value::Uint8(l), Value::Uint8(r)) => l == r,
323			(Value::Uint16(l), Value::Uint16(r)) => l == r,
324			(Value::Date(l), Value::Date(r)) => l == r,
325			(Value::DateTime(l), Value::DateTime(r)) => l == r,
326			(Value::Time(l), Value::Time(r)) => l == r,
327			(Value::Duration(l), Value::Duration(r)) => l == r,
328			(Value::IdentityId(l), Value::IdentityId(r)) => l == r,
329			(Value::Uuid4(l), Value::Uuid4(r)) => l == r,
330			(Value::Uuid7(l), Value::Uuid7(r)) => l == r,
331			(Value::Blob(l), Value::Blob(r)) => l == r,
332			(Value::Int(l), Value::Int(r)) => l == r,
333			(Value::Uint(l), Value::Uint(r)) => l == r,
334			(Value::Decimal(l), Value::Decimal(r)) => l == r,
335			(Value::Any(l), Value::Any(r)) => l == r,
336			(Value::DictionaryId(l), Value::DictionaryId(r)) => l == r,
337			(Value::Type(l), Value::Type(r)) => l == r,
338			(Value::List(l), Value::List(r)) => l == r,
339			(Value::Record(l), Value::Record(r)) => l == r,
340			(Value::Tuple(l), Value::Tuple(r)) => l == r,
341			_ => false,
342		}
343	}
344}
345
346impl Eq for Value {}
347
348impl hash::Hash for Value {
349	fn hash<H: hash::Hasher>(&self, state: &mut H) {
350		mem::discriminant(self).hash(state);
351		match self {
352			Value::None {
353				..
354			} => {} // All Nones hash identically
355			Value::Boolean(v) => v.hash(state),
356			Value::Float4(v) => v.hash(state),
357			Value::Float8(v) => v.hash(state),
358			Value::Int1(v) => v.hash(state),
359			Value::Int2(v) => v.hash(state),
360			Value::Int4(v) => v.hash(state),
361			Value::Int8(v) => v.hash(state),
362			Value::Int16(v) => v.hash(state),
363			Value::Utf8(v) => v.hash(state),
364			Value::Uint1(v) => v.hash(state),
365			Value::Uint2(v) => v.hash(state),
366			Value::Uint4(v) => v.hash(state),
367			Value::Uint8(v) => v.hash(state),
368			Value::Uint16(v) => v.hash(state),
369			Value::Date(v) => v.hash(state),
370			Value::DateTime(v) => v.hash(state),
371			Value::Time(v) => v.hash(state),
372			Value::Duration(v) => v.hash(state),
373			Value::IdentityId(v) => v.hash(state),
374			Value::Uuid4(v) => v.hash(state),
375			Value::Uuid7(v) => v.hash(state),
376			Value::Blob(v) => v.hash(state),
377			Value::Int(v) => v.hash(state),
378			Value::Uint(v) => v.hash(state),
379			Value::Decimal(v) => v.hash(state),
380			Value::Any(v) => v.hash(state),
381			Value::DictionaryId(v) => v.hash(state),
382			Value::Type(v) => v.hash(state),
383			Value::List(v) => v.hash(state),
384			Value::Record(fields) => {
385				for (k, v) in fields {
386					k.hash(state);
387					v.hash(state);
388				}
389			}
390			Value::Tuple(v) => v.hash(state),
391		}
392	}
393}
394
395impl PartialOrd for Value {
396	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
397		match (self, other) {
398			(Value::Boolean(l), Value::Boolean(r)) => l.partial_cmp(r),
399			(Value::Float4(l), Value::Float4(r)) => l.partial_cmp(r),
400			(Value::Float8(l), Value::Float8(r)) => l.partial_cmp(r),
401			(Value::Int1(l), Value::Int1(r)) => l.partial_cmp(r),
402			(Value::Int2(l), Value::Int2(r)) => l.partial_cmp(r),
403			(Value::Int4(l), Value::Int4(r)) => l.partial_cmp(r),
404			(Value::Int8(l), Value::Int8(r)) => l.partial_cmp(r),
405			(Value::Int16(l), Value::Int16(r)) => l.partial_cmp(r),
406			(Value::Utf8(l), Value::Utf8(r)) => l.partial_cmp(r),
407			(Value::Uint1(l), Value::Uint1(r)) => l.partial_cmp(r),
408			(Value::Uint2(l), Value::Uint2(r)) => l.partial_cmp(r),
409			(Value::Uint4(l), Value::Uint4(r)) => l.partial_cmp(r),
410			(Value::Uint8(l), Value::Uint8(r)) => l.partial_cmp(r),
411			(Value::Uint16(l), Value::Uint16(r)) => l.partial_cmp(r),
412			(Value::Date(l), Value::Date(r)) => l.partial_cmp(r),
413			(Value::DateTime(l), Value::DateTime(r)) => l.partial_cmp(r),
414			(Value::Time(l), Value::Time(r)) => l.partial_cmp(r),
415			(Value::Duration(l), Value::Duration(r)) => l.partial_cmp(r),
416			(Value::IdentityId(l), Value::IdentityId(r)) => l.partial_cmp(r),
417			(Value::Uuid4(l), Value::Uuid4(r)) => l.partial_cmp(r),
418			(Value::Uuid7(l), Value::Uuid7(r)) => l.partial_cmp(r),
419			(Value::Blob(l), Value::Blob(r)) => l.partial_cmp(r),
420			(Value::Int(l), Value::Int(r)) => l.partial_cmp(r),
421			(Value::Uint(l), Value::Uint(r)) => l.partial_cmp(r),
422			(Value::Decimal(l), Value::Decimal(r)) => l.partial_cmp(r),
423			(Value::DictionaryId(l), Value::DictionaryId(r)) => l.to_u128().partial_cmp(&r.to_u128()),
424			(Value::Type(l), Value::Type(r)) => l.partial_cmp(r),
425			(Value::List(_), Value::List(_)) => None,     // Lists are not orderable
426			(Value::Record(_), Value::Record(_)) => None, // Records are not orderable
427			(Value::Tuple(_), Value::Tuple(_)) => None,   // Tuples are not orderable
428			(Value::Any(_), Value::Any(_)) => None,       // Any values are not comparable
429			(
430				Value::None {
431					..
432				},
433				Value::None {
434					..
435				},
436			) => Some(Ordering::Equal),
437			// None sorts after all other values (similar to NULL in SQL)
438			(
439				Value::None {
440					..
441				},
442				_,
443			) => Some(Ordering::Greater),
444			(
445				_,
446				Value::None {
447					..
448				},
449			) => Some(Ordering::Less),
450			(left, right) => {
451				unimplemented!("partial cmp {left:?} {right:?}")
452			}
453		}
454	}
455}
456
457impl Ord for Value {
458	fn cmp(&self, other: &Self) -> Ordering {
459		match (self, other) {
460			(
461				Value::None {
462					..
463				},
464				Value::None {
465					..
466				},
467			) => Ordering::Equal,
468			(
469				Value::None {
470					..
471				},
472				_,
473			) => Ordering::Greater,
474			(
475				_,
476				Value::None {
477					..
478				},
479			) => Ordering::Less,
480			(Value::Boolean(l), Value::Boolean(r)) => l.cmp(r),
481			(Value::Float4(l), Value::Float4(r)) => l.cmp(r),
482			(Value::Float8(l), Value::Float8(r)) => l.cmp(r),
483			(Value::Int1(l), Value::Int1(r)) => l.cmp(r),
484			(Value::Int2(l), Value::Int2(r)) => l.cmp(r),
485			(Value::Int4(l), Value::Int4(r)) => l.cmp(r),
486			(Value::Int8(l), Value::Int8(r)) => l.cmp(r),
487			(Value::Int16(l), Value::Int16(r)) => l.cmp(r),
488			(Value::Utf8(l), Value::Utf8(r)) => l.cmp(r),
489			(Value::Uint1(l), Value::Uint1(r)) => l.cmp(r),
490			(Value::Uint2(l), Value::Uint2(r)) => l.cmp(r),
491			(Value::Uint4(l), Value::Uint4(r)) => l.cmp(r),
492			(Value::Uint8(l), Value::Uint8(r)) => l.cmp(r),
493			(Value::Uint16(l), Value::Uint16(r)) => l.cmp(r),
494			(Value::Date(l), Value::Date(r)) => l.cmp(r),
495			(Value::DateTime(l), Value::DateTime(r)) => l.cmp(r),
496			(Value::Time(l), Value::Time(r)) => l.cmp(r),
497			(Value::Duration(l), Value::Duration(r)) => l.cmp(r),
498			(Value::IdentityId(l), Value::IdentityId(r)) => l.cmp(r),
499			(Value::Uuid4(l), Value::Uuid4(r)) => l.cmp(r),
500			(Value::Uuid7(l), Value::Uuid7(r)) => l.cmp(r),
501			(Value::Blob(l), Value::Blob(r)) => l.cmp(r),
502			(Value::Int(l), Value::Int(r)) => l.cmp(r),
503			(Value::Uint(l), Value::Uint(r)) => l.cmp(r),
504			(Value::Decimal(l), Value::Decimal(r)) => l.cmp(r),
505			(Value::DictionaryId(l), Value::DictionaryId(r)) => l.to_u128().cmp(&r.to_u128()),
506			(Value::Type(l), Value::Type(r)) => l.cmp(r),
507			(Value::List(_), Value::List(_)) => unreachable!("List values are not orderable"),
508			(Value::Record(_), Value::Record(_)) => unreachable!("Record values are not orderable"),
509			(Value::Tuple(_), Value::Tuple(_)) => unreachable!("Tuple values are not orderable"),
510			(Value::Any(_), Value::Any(_)) => unreachable!("Any values are not orderable"),
511			_ => unimplemented!(),
512		}
513	}
514}
515
516impl Display for Value {
517	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
518		match self {
519			Value::Boolean(true) => f.write_str("true"),
520			Value::Boolean(false) => f.write_str("false"),
521			Value::Float4(value) => Display::fmt(value, f),
522			Value::Float8(value) => Display::fmt(value, f),
523			Value::Int1(value) => Display::fmt(value, f),
524			Value::Int2(value) => Display::fmt(value, f),
525			Value::Int4(value) => Display::fmt(value, f),
526			Value::Int8(value) => Display::fmt(value, f),
527			Value::Int16(value) => Display::fmt(value, f),
528			Value::Utf8(value) => Display::fmt(value, f),
529			Value::Uint1(value) => Display::fmt(value, f),
530			Value::Uint2(value) => Display::fmt(value, f),
531			Value::Uint4(value) => Display::fmt(value, f),
532			Value::Uint8(value) => Display::fmt(value, f),
533			Value::Uint16(value) => Display::fmt(value, f),
534			Value::Date(value) => Display::fmt(value, f),
535			Value::DateTime(value) => Display::fmt(value, f),
536			Value::Time(value) => Display::fmt(value, f),
537			Value::Duration(value) => Display::fmt(value, f),
538			Value::IdentityId(value) => Display::fmt(value, f),
539			Value::Uuid4(value) => Display::fmt(value, f),
540			Value::Uuid7(value) => Display::fmt(value, f),
541			Value::Blob(value) => Display::fmt(value, f),
542			Value::Int(value) => Display::fmt(value, f),
543			Value::Uint(value) => Display::fmt(value, f),
544			Value::Decimal(value) => Display::fmt(value, f),
545			Value::Any(value) => Display::fmt(value, f),
546			Value::DictionaryId(value) => Display::fmt(value, f),
547			Value::Type(value) => Display::fmt(value, f),
548			Value::List(items) => {
549				f.write_str("[")?;
550				for (i, item) in items.iter().enumerate() {
551					if i > 0 {
552						f.write_str(", ")?;
553					}
554					Display::fmt(item, f)?;
555				}
556				f.write_str("]")
557			}
558			Value::Record(fields) => {
559				f.write_str("{")?;
560				for (i, (key, value)) in fields.iter().enumerate() {
561					if i > 0 {
562						f.write_str(", ")?;
563					}
564					write!(f, "{}: {}", key, value)?;
565				}
566				f.write_str("}")
567			}
568			Value::Tuple(items) => {
569				f.write_str("(")?;
570				for (i, item) in items.iter().enumerate() {
571					if i > 0 {
572						f.write_str(", ")?;
573					}
574					Display::fmt(item, f)?;
575				}
576				f.write_str(")")
577			}
578			Value::None {
579				..
580			} => f.write_str("none"),
581		}
582	}
583}
584
585impl Value {
586	pub fn get_type(&self) -> Type {
587		match self {
588			Value::None {
589				inner,
590			} => Type::Option(Box::new(inner.clone())),
591			Value::Boolean(_) => Type::Boolean,
592			Value::Float4(_) => Type::Float4,
593			Value::Float8(_) => Type::Float8,
594			Value::Int1(_) => Type::Int1,
595			Value::Int2(_) => Type::Int2,
596			Value::Int4(_) => Type::Int4,
597			Value::Int8(_) => Type::Int8,
598			Value::Int16(_) => Type::Int16,
599			Value::Utf8(_) => Type::Utf8,
600			Value::Uint1(_) => Type::Uint1,
601			Value::Uint2(_) => Type::Uint2,
602			Value::Uint4(_) => Type::Uint4,
603			Value::Uint8(_) => Type::Uint8,
604			Value::Uint16(_) => Type::Uint16,
605			Value::Date(_) => Type::Date,
606			Value::DateTime(_) => Type::DateTime,
607			Value::Time(_) => Type::Time,
608			Value::Duration(_) => Type::Duration,
609			Value::IdentityId(_) => Type::IdentityId,
610			Value::Uuid4(_) => Type::Uuid4,
611			Value::Uuid7(_) => Type::Uuid7,
612			Value::Blob(_) => Type::Blob,
613			Value::Int(_) => Type::Int,
614			Value::Uint(_) => Type::Uint,
615			Value::Decimal(_) => Type::Decimal,
616			Value::Any(_) => Type::Any,
617			Value::DictionaryId(_) => Type::DictionaryId,
618			Value::Type(t) => t.clone(),
619			Value::List(items) => {
620				let element_type = items.first().map(|v| v.get_type()).unwrap_or(Type::Any);
621				Type::list_of(element_type)
622			}
623			Value::Record(fields) => {
624				Type::Record(fields.iter().map(|(k, v)| (k.clone(), v.get_type())).collect())
625			}
626			Value::Tuple(items) => Type::Tuple(items.iter().map(|v| v.get_type()).collect()),
627		}
628	}
629}
630
631#[cfg(test)]
632mod tests {
633	use std::str::FromStr;
634
635	use super::*;
636
637	// Happy path — one per numeric type
638
639	#[test]
640	fn to_usize_uint1() {
641		assert_eq!(Value::uint1(42u8).to_usize(), Some(42));
642	}
643
644	#[test]
645	fn to_usize_uint2() {
646		assert_eq!(Value::uint2(1000u16).to_usize(), Some(1000));
647	}
648
649	#[test]
650	fn to_usize_uint4() {
651		assert_eq!(Value::uint4(100_000u32).to_usize(), Some(100_000));
652	}
653
654	#[test]
655	fn to_usize_uint8() {
656		assert_eq!(Value::uint8(1_000_000u64).to_usize(), Some(1_000_000));
657	}
658
659	#[test]
660	fn to_usize_uint16() {
661		assert_eq!(Value::Uint16(500u128).to_usize(), Some(500));
662	}
663
664	#[test]
665	fn to_usize_int1() {
666		assert_eq!(Value::int1(100i8).to_usize(), Some(100));
667	}
668
669	#[test]
670	fn to_usize_int2() {
671		assert_eq!(Value::int2(5000i16).to_usize(), Some(5000));
672	}
673
674	#[test]
675	fn to_usize_int4() {
676		assert_eq!(Value::int4(50_000i32).to_usize(), Some(50_000));
677	}
678
679	#[test]
680	fn to_usize_int8() {
681		assert_eq!(Value::int8(1_000_000i64).to_usize(), Some(1_000_000));
682	}
683
684	#[test]
685	fn to_usize_int16() {
686		assert_eq!(Value::Int16(999i128).to_usize(), Some(999));
687	}
688
689	#[test]
690	fn to_usize_float4() {
691		assert_eq!(Value::float4(42.0f32).to_usize(), Some(42));
692	}
693
694	#[test]
695	fn to_usize_float8() {
696		assert_eq!(Value::float8(42.0f64).to_usize(), Some(42));
697	}
698
699	#[test]
700	fn to_usize_int_bigint() {
701		assert_eq!(Value::Int(Int::from_i64(42)).to_usize(), Some(42));
702	}
703
704	#[test]
705	fn to_usize_uint_bigint() {
706		assert_eq!(Value::Uint(Uint::from_u64(42)).to_usize(), Some(42));
707	}
708
709	#[test]
710	fn to_usize_decimal() {
711		assert_eq!(Value::Decimal(Decimal::from_i64(42)).to_usize(), Some(42));
712	}
713
714	// Edge cases & errors — negative numbers
715
716	#[test]
717	fn to_usize_int1_negative() {
718		assert_eq!(Value::int1(-1i8).to_usize(), None);
719	}
720
721	#[test]
722	fn to_usize_int2_negative() {
723		assert_eq!(Value::int2(-100i16).to_usize(), None);
724	}
725
726	#[test]
727	fn to_usize_int4_negative() {
728		assert_eq!(Value::int4(-1i32).to_usize(), None);
729	}
730
731	#[test]
732	fn to_usize_int8_negative() {
733		assert_eq!(Value::int8(-1i64).to_usize(), None);
734	}
735
736	#[test]
737	fn to_usize_int16_negative() {
738		assert_eq!(Value::Int16(-1i128).to_usize(), None);
739	}
740
741	#[test]
742	fn to_usize_float4_negative() {
743		assert_eq!(Value::float4(-1.0f32).to_usize(), None);
744	}
745
746	#[test]
747	fn to_usize_float8_negative() {
748		assert_eq!(Value::float8(-1.0f64).to_usize(), None);
749	}
750
751	#[test]
752	fn to_usize_int_bigint_negative() {
753		assert_eq!(Value::Int(Int::from_i64(-5)).to_usize(), None);
754	}
755
756	// Edge cases — zero boundary
757
758	#[test]
759	fn to_usize_zero() {
760		assert_eq!(Value::uint1(0u8).to_usize(), Some(0));
761	}
762
763	#[test]
764	fn to_usize_int1_zero() {
765		assert_eq!(Value::int1(0i8).to_usize(), Some(0));
766	}
767
768	#[test]
769	fn to_usize_float4_zero() {
770		assert_eq!(Value::float4(0.0f32).to_usize(), Some(0));
771	}
772
773	// Edge cases — non-numeric types return None
774
775	#[test]
776	fn to_usize_boolean_none() {
777		assert_eq!(Value::bool(true).to_usize(), None);
778	}
779
780	#[test]
781	fn to_usize_utf8_integer() {
782		assert_eq!(Value::utf8("42").to_usize(), Some(42));
783	}
784
785	#[test]
786	fn to_usize_utf8_float() {
787		assert_eq!(Value::utf8("3.7").to_usize(), Some(3));
788	}
789
790	#[test]
791	fn to_usize_utf8_negative() {
792		assert_eq!(Value::utf8("-5").to_usize(), None);
793	}
794
795	#[test]
796	fn to_usize_utf8_negative_float() {
797		assert_eq!(Value::utf8("-1.5").to_usize(), None);
798	}
799
800	#[test]
801	fn to_usize_utf8_whitespace() {
802		assert_eq!(Value::utf8("  42  ").to_usize(), Some(42));
803	}
804
805	#[test]
806	fn to_usize_utf8_zero() {
807		assert_eq!(Value::utf8("0").to_usize(), Some(0));
808	}
809
810	#[test]
811	fn to_usize_utf8_non_numeric() {
812		assert_eq!(Value::utf8("hello").to_usize(), None);
813	}
814
815	#[test]
816	fn to_usize_utf8_empty() {
817		assert_eq!(Value::utf8("").to_usize(), None);
818	}
819
820	#[test]
821	fn to_usize_none_none() {
822		assert_eq!(Value::none().to_usize(), None);
823	}
824
825	// Edge cases — fractional truncation
826
827	#[test]
828	fn to_usize_float8_fractional() {
829		assert_eq!(Value::float8(3.7f64).to_usize(), Some(3));
830	}
831
832	#[test]
833	fn to_usize_decimal_fractional() {
834		assert_eq!(Value::Decimal(Decimal::from_str("3.7").unwrap()).to_usize(), Some(3));
835	}
836}