1mod discriminant;
2
3use std::borrow::Cow;
4use std::str::FromStr;
5use std::sync::OnceLock;
6
7use serde::{Deserialize, Serialize};
8
9use super::types;
10use crate::memory::{
11 DEFAULT_ALIGNMENT, DataSize, DecodeError, Encode, MSize, MemoryError, MemoryResult, PageOffset,
12};
13
14#[cfg_attr(feature = "candid", derive(candid::CandidType))]
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
17pub enum Value {
18 Blob(types::Blob),
19 Boolean(types::Boolean),
20 Date(types::Date),
21 DateTime(types::DateTime),
22 Decimal(types::Decimal),
23 Int8(types::Int8),
24 Int16(types::Int16),
25 Int32(types::Int32),
26 Int64(types::Int64),
27 Json(types::Json),
28 Null,
29 Text(types::Text),
30 Uint8(types::Uint8),
31 Uint16(types::Uint16),
32 Uint32(types::Uint32),
33 Uint64(types::Uint64),
34 Uuid(types::Uuid),
35 Custom(crate::dbms::custom_value::CustomValue),
36}
37
38impl FromStr for Value {
39 type Err = ();
40
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 Ok(Self::Text(s.into()))
43 }
44}
45
46macro_rules! impl_conv_for_value {
48 ($variant:ident, $ty:ty, $name:ident, $test_name:ident) => {
49 impl From<$ty> for Value {
50 fn from(value: $ty) -> Self {
51 Value::$variant(value)
52 }
53 }
54
55 impl Value {
56 pub fn $name(&self) -> Option<&$ty> {
58 if let Value::$variant(v) = self {
59 Some(v)
60 } else {
61 None
62 }
63 }
64 }
65
66 #[cfg(test)]
67 mod $test_name {
68 use super::*;
69
70 #[test]
71 fn test_value_conversion() {
72 let value_instance: $ty = Default::default();
73 let value: Value = value_instance.clone().into();
74 assert_eq!(value.$name(), Some(&value_instance));
75 }
76 }
77 };
78}
79
80macro_rules! value_from_primitive {
81 ($variant:ident, $primitive:ty, $test_name:ident) => {
82 value_from_primitive!($variant, $primitive, $test_name, Default::default());
83 };
84
85 ($variant:ident, $primitive:ty, $test_name:ident, $default_value:expr) => {
86 impl From<$primitive> for Value {
87 fn from(value: $primitive) -> Self {
88 Value::$variant($crate::prelude::$variant(value.into()))
89 }
90 }
91
92 #[cfg(test)]
93 mod $test_name {
94 use super::*;
95
96 #[test]
97 fn test_value_from_primitive() {
98 let primitive_value: $primitive = $default_value;
99 if let Value::$variant(inner_value) = Value::from(primitive_value.clone()) {
100 assert_eq!(inner_value.0, primitive_value);
101 } else {
102 panic!("Value variant does not match");
103 }
104 }
105 }
106 };
107}
108
109impl_conv_for_value!(Blob, types::Blob, as_blob, tests_blob);
111impl_conv_for_value!(Boolean, types::Boolean, as_boolean, tests_boolean);
112impl_conv_for_value!(Date, types::Date, as_date, tests_date);
113impl_conv_for_value!(DateTime, types::DateTime, as_datetime, tests_datetime);
114impl_conv_for_value!(Decimal, types::Decimal, as_decimal, tests_decimal);
115impl_conv_for_value!(Int8, types::Int8, as_int8, tests_int8);
116impl_conv_for_value!(Int16, types::Int16, as_int16, tests_int16);
117impl_conv_for_value!(Int32, types::Int32, as_int32, tests_int32);
118impl_conv_for_value!(Int64, types::Int64, as_int64, tests_int64);
119impl_conv_for_value!(Json, types::Json, as_json, tests_json);
120impl_conv_for_value!(Text, types::Text, as_text, tests_text);
121impl_conv_for_value!(Uint8, types::Uint8, as_uint8, tests_uint8);
122impl_conv_for_value!(Uint16, types::Uint16, as_uint16, tests_uint16);
123impl_conv_for_value!(Uint32, types::Uint32, as_uint32, tests_uint32);
124impl_conv_for_value!(Uint64, types::Uint64, as_uint64, tests_uint64);
125impl_conv_for_value!(Uuid, types::Uuid, as_uuid, tests_uuid);
126
127value_from_primitive!(Blob, &[u8], tests_blob_primitive_slice);
129value_from_primitive!(Blob, Vec<u8>, tests_blob_primitive);
130value_from_primitive!(Boolean, bool, tests_boolean_primitive);
131value_from_primitive!(Decimal, rust_decimal::Decimal, tests_decimal_primitive);
132value_from_primitive!(Int8, i8, tests_int8_primitive);
133value_from_primitive!(Int16, i16, tests_int16_primitive);
134value_from_primitive!(Int32, i32, tests_int32_primitive);
135value_from_primitive!(Int64, i64, tests_int64_primitive);
136value_from_primitive!(Uint8, u8, tests_uint8_primitive);
137value_from_primitive!(Uint16, u16, tests_uint16_primitive);
138value_from_primitive!(Uint32, u32, tests_uint32_primitive);
139value_from_primitive!(Uint64, u64, tests_uint64_primitive);
140value_from_primitive!(Text, String, tests_text_primitive_string);
141value_from_primitive!(Text, &str, tests_text_primitive_str);
142value_from_primitive!(Uuid, uuid::Uuid, tests_uuid_primitive);
143
144impl Value {
145 pub fn is_null(&self) -> bool {
147 matches!(self, Value::Null)
148 }
149
150 pub fn type_name(&self) -> &'static str {
152 match self {
153 Value::Blob(_) => "Blob",
154 Value::Boolean(_) => "Boolean",
155 Value::Date(_) => "Date",
156 Value::DateTime(_) => "DateTime",
157 Value::Decimal(_) => "Decimal",
158 Value::Int8(_) => "Int8",
159 Value::Int16(_) => "Int16",
160 Value::Int32(_) => "Int32",
161 Value::Int64(_) => "Int64",
162 Value::Json(_) => "Json",
163 Value::Null => "Null",
164 Value::Text(_) => "Text",
165 Value::Uint8(_) => "Uint8",
166 Value::Uint16(_) => "Uint16",
167 Value::Uint32(_) => "Uint32",
168 Value::Uint64(_) => "Uint64",
169 Value::Uuid(_) => "Uuid",
170 Value::Custom(cv) => {
171 const MAX_CACHE_ENTRIES: usize = 64;
177 static CACHE: OnceLock<
178 std::sync::Mutex<std::collections::HashMap<String, &'static str>>,
179 > = OnceLock::new();
180 let cache =
181 CACHE.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
182 let mut map = cache.lock().unwrap_or_else(|e| e.into_inner());
183 if map.len() >= MAX_CACHE_ENTRIES && !map.contains_key(&cv.type_tag) {
184 return "Custom(?)";
185 }
186 map.entry(cv.type_tag.clone()).or_insert_with(|| {
187 let s = format!("Custom({})", cv.type_tag);
188 s.leak()
189 })
190 }
191 }
192 }
193
194 pub fn as_custom(&self) -> Option<&crate::dbms::custom_value::CustomValue> {
196 match self {
197 Value::Custom(v) => Some(v),
198 _ => None,
199 }
200 }
201
202 pub fn as_custom_type<T: crate::dbms::types::CustomDataType>(&self) -> Option<T> {
207 self.as_custom()
208 .filter(|cv| cv.type_tag == T::TYPE_TAG)
209 .and_then(|cv| T::decode(std::borrow::Cow::Borrowed(&cv.encoded)).ok())
210 }
211}
212
213impl Encode for Value {
221 const SIZE: DataSize = DataSize::Dynamic;
222 const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
223
224 fn encode(&'_ self) -> Cow<'_, [u8]> {
225 match self {
226 Value::Blob(v) => encode_with_discriminant(discriminant::BLOB, v.encode()),
227 Value::Boolean(v) => encode_with_discriminant(discriminant::BOOLEAN, v.encode()),
228 Value::Date(v) => encode_with_discriminant(discriminant::DATE, v.encode()),
229 Value::DateTime(v) => encode_with_discriminant(discriminant::DATE_TIME, v.encode()),
230 Value::Decimal(v) => encode_with_discriminant(discriminant::DECIMAL, v.encode()),
231 Value::Int8(v) => encode_with_discriminant(discriminant::INT8, v.encode()),
232 Value::Int16(v) => encode_with_discriminant(discriminant::INT16, v.encode()),
233 Value::Int32(v) => encode_with_discriminant(discriminant::INT32, v.encode()),
234 Value::Int64(v) => encode_with_discriminant(discriminant::INT64, v.encode()),
235 Value::Json(v) => encode_with_discriminant(discriminant::JSON, v.encode()),
236 Value::Null => Cow::Owned(vec![discriminant::NULL]),
237 Value::Text(v) => encode_with_discriminant(discriminant::TEXT, v.encode()),
238 Value::Uint8(v) => encode_with_discriminant(discriminant::UINT8, v.encode()),
239 Value::Uint16(v) => encode_with_discriminant(discriminant::UINT16, v.encode()),
240 Value::Uint32(v) => encode_with_discriminant(discriminant::UINT32, v.encode()),
241 Value::Uint64(v) => encode_with_discriminant(discriminant::UINT64, v.encode()),
242 Value::Uuid(v) => encode_with_discriminant(discriminant::UUID, v.encode()),
243 Value::Custom(cv) => {
244 let tag_bytes = cv.type_tag.as_bytes();
245 let total = 1 + 2 + tag_bytes.len() + 2 + cv.encoded.len();
246 let mut buf = Vec::with_capacity(total);
247 buf.push(discriminant::CUSTOM);
248 buf.extend_from_slice(&crate::memory::length_prefix(tag_bytes.len()));
249 buf.extend_from_slice(tag_bytes);
250 buf.extend_from_slice(&crate::memory::length_prefix(cv.encoded.len()));
251 buf.extend_from_slice(&cv.encoded);
252 Cow::Owned(buf)
253 }
254 }
255 }
256
257 fn decode(data: Cow<[u8]>) -> MemoryResult<Self> {
258 if data.is_empty() {
259 return Err(MemoryError::DecodeError(DecodeError::TooShort));
260 }
261
262 let disc = data[0];
263 let rest = Cow::Owned(data[1..].to_vec());
264
265 match disc {
266 discriminant::BLOB => types::Blob::decode(rest).map(Value::Blob),
267 discriminant::BOOLEAN => types::Boolean::decode(rest).map(Value::Boolean),
268 discriminant::DATE => types::Date::decode(rest).map(Value::Date),
269 discriminant::DATE_TIME => types::DateTime::decode(rest).map(Value::DateTime),
270 discriminant::DECIMAL => types::Decimal::decode(rest).map(Value::Decimal),
271 discriminant::INT8 => types::Int8::decode(rest).map(Value::Int8),
272 discriminant::INT16 => types::Int16::decode(rest).map(Value::Int16),
273 discriminant::INT32 => types::Int32::decode(rest).map(Value::Int32),
274 discriminant::INT64 => types::Int64::decode(rest).map(Value::Int64),
275 discriminant::JSON => types::Json::decode(rest).map(Value::Json),
276 discriminant::NULL => Ok(Value::Null),
277 discriminant::TEXT => types::Text::decode(rest).map(Value::Text),
278 discriminant::UINT8 => types::Uint8::decode(rest).map(Value::Uint8),
279 discriminant::UINT16 => types::Uint16::decode(rest).map(Value::Uint16),
280 discriminant::UINT32 => types::Uint32::decode(rest).map(Value::Uint32),
281 discriminant::UINT64 => types::Uint64::decode(rest).map(Value::Uint64),
282 discriminant::UUID => types::Uuid::decode(rest).map(Value::Uuid),
283 discriminant::CUSTOM => decode_custom_value(&data[1..]),
284 other => Err(MemoryError::DecodeError(DecodeError::InvalidDiscriminant(
285 other,
286 ))),
287 }
288 }
289
290 fn size(&self) -> MSize {
291 let inner = match self {
292 Value::Blob(v) => Encode::size(v),
293 Value::Boolean(v) => Encode::size(v),
294 Value::Date(v) => Encode::size(v),
295 Value::DateTime(v) => Encode::size(v),
296 Value::Decimal(v) => Encode::size(v),
297 Value::Int8(v) => Encode::size(v),
298 Value::Int16(v) => Encode::size(v),
299 Value::Int32(v) => Encode::size(v),
300 Value::Int64(v) => Encode::size(v),
301 Value::Json(v) => Encode::size(v),
302 Value::Null => 0,
303 Value::Text(v) => Encode::size(v),
304 Value::Uint8(v) => Encode::size(v),
305 Value::Uint16(v) => Encode::size(v),
306 Value::Uint32(v) => Encode::size(v),
307 Value::Uint64(v) => Encode::size(v),
308 Value::Uuid(v) => Encode::size(v),
309 Value::Custom(cv) => {
310 crate::memory::saturating_size(
312 2,
313 cv.type_tag
314 .len()
315 .saturating_add(2)
316 .saturating_add(cv.encoded.len()),
317 )
318 }
319 };
320 inner.saturating_add(1)
322 }
323}
324
325fn encode_with_discriminant(disc: u8, inner: Cow<[u8]>) -> Cow<'static, [u8]> {
327 let mut buf = Vec::with_capacity(1 + inner.len());
328 buf.push(disc);
329 buf.extend_from_slice(&inner);
330 Cow::Owned(buf)
331}
332
333fn decode_custom_value(data: &[u8]) -> MemoryResult<Value> {
335 if data.len() < 2 {
336 return Err(MemoryError::DecodeError(DecodeError::TooShort));
337 }
338 let tag_len = u16::from_le_bytes([data[0], data[1]]) as usize;
339 if data.len() < 2 + tag_len + 2 {
340 return Err(MemoryError::DecodeError(DecodeError::TooShort));
341 }
342 let type_tag = String::from_utf8(data[2..2 + tag_len].to_vec())?;
343 let data_offset = 2 + tag_len;
344 let data_len = u16::from_le_bytes([data[data_offset], data[data_offset + 1]]) as usize;
345 if data.len() < data_offset + 2 + data_len {
346 return Err(MemoryError::DecodeError(DecodeError::TooShort));
347 }
348 let encoded = data[data_offset + 2..data_offset + 2 + data_len].to_vec();
349 Ok(Value::Custom(crate::dbms::custom_value::CustomValue {
350 type_tag,
351 encoded,
352 display: String::new(),
353 }))
354}
355
356const MIN_ENCODED_VEC_ENTRY_SIZE: usize = 4 + 1;
359
360impl Encode for Vec<Value> {
365 const SIZE: DataSize = DataSize::Dynamic;
366 const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
367
368 fn encode(&'_ self) -> Cow<'_, [u8]> {
369 let count = self.len() as u32;
370 let mut buf = Vec::new();
371 buf.extend_from_slice(&count.to_le_bytes());
372 for value in self {
373 let encoded = Encode::encode(value);
374 let size = encoded.len() as u32;
375 buf.extend_from_slice(&size.to_le_bytes());
376 buf.extend_from_slice(&encoded);
377 }
378 Cow::Owned(buf)
379 }
380
381 fn decode(data: Cow<[u8]>) -> MemoryResult<Self> {
382 if data.len() < 4 {
383 return Err(MemoryError::DecodeError(DecodeError::TooShort));
384 }
385 let count = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
386 let mut offset = 4;
387 if count > (data.len() - offset) / MIN_ENCODED_VEC_ENTRY_SIZE {
390 return Err(MemoryError::DecodeError(DecodeError::TooShort));
391 }
392 let mut values = Vec::new();
393 values
396 .try_reserve_exact(count)
397 .map_err(|_| MemoryError::DecodeError(DecodeError::TooShort))?;
398 for _ in 0..count {
399 if offset + 4 > data.len() {
400 return Err(MemoryError::DecodeError(DecodeError::TooShort));
401 }
402 let size = u32::from_le_bytes([
403 data[offset],
404 data[offset + 1],
405 data[offset + 2],
406 data[offset + 3],
407 ]) as usize;
408 offset += 4;
409 if offset + size > data.len() {
410 return Err(MemoryError::DecodeError(DecodeError::TooShort));
411 }
412 let value = Value::decode(Cow::Owned(data[offset..offset + size].to_vec()))?;
413 values.push(value);
414 offset += size;
415 }
416 Ok(values)
417 }
418
419 fn size(&self) -> MSize {
420 let mut total: MSize = 4; for value in self {
422 total = total.saturating_add(4).saturating_add(Encode::size(value));
424 }
425 total
426 }
427}
428
429#[cfg(test)]
430mod tests {
431
432 use uuid::Uuid;
433
434 use super::*;
435
436 #[test]
437 fn test_null() {
438 let int_value: Value = types::Int32(42).into();
439 assert!(!int_value.is_null());
440
441 let null_value = Value::Null;
442 assert!(null_value.is_null());
443 }
444
445 #[test]
446 fn test_value_conversion_blob() {
447 let blob = types::Blob(vec![1, 2, 3]);
448 let value: Value = blob.clone().into();
449 assert_eq!(value.as_blob(), Some(&blob));
450 }
451
452 #[test]
453 fn test_value_conversion_boolean() {
454 let boolean = types::Boolean(true);
455 let value: Value = boolean.into();
456 assert_eq!(value.as_boolean(), Some(&boolean));
457 }
458
459 #[test]
460 fn test_value_conversion_date() {
461 let date = types::Date {
462 year: 2023,
463 month: 3,
464 day: 15,
465 }; let value: Value = date.into();
467 assert_eq!(value.as_date(), Some(&date));
468 }
469
470 #[test]
471 fn test_value_conversion_datetime() {
472 let datetime = types::DateTime {
473 year: 2023,
474 month: 3,
475 day: 15,
476 hour: 12,
477 minute: 30,
478 second: 45,
479 microsecond: 123456,
480 timezone_offset_minutes: 0,
481 }; let value: Value = datetime.into();
483 assert_eq!(value.as_datetime(), Some(&datetime));
484 }
485
486 #[test]
487 fn test_value_conversion_decimal() {
488 let decimal = types::Decimal(rust_decimal::Decimal::new(12345, 2)); let value: Value = decimal.into();
490 assert_eq!(value.as_decimal(), Some(&decimal));
491 }
492
493 #[test]
494 fn test_value_conversion_int32() {
495 let int32 = types::Int32(1234567890);
496 let value: Value = int32.into();
497 assert_eq!(value.as_int32(), Some(&int32));
498 }
499
500 #[test]
501 fn test_value_conversion_int64() {
502 let int64 = types::Int64(1234567890);
503 let value: Value = int64.into();
504 assert_eq!(value.as_int64(), Some(&int64));
505 }
506
507 #[test]
508 fn test_value_conversion_text() {
509 let text = types::Text("Hello, World!".to_string());
510 let value: Value = text.clone().into();
511 assert_eq!(value.as_text(), Some(&text));
512 }
513
514 #[test]
515 fn test_value_conversion_uint32() {
516 let uint32 = types::Uint32(123456);
517 let value: Value = uint32.into();
518 assert_eq!(value.as_uint32(), Some(&uint32));
519 }
520
521 #[test]
522 fn test_value_conversion_uint64() {
523 let uint64 = types::Uint64(12345678901234);
524 let value: Value = uint64.into();
525 assert_eq!(value.as_uint64(), Some(&uint64));
526 }
527
528 #[test]
529 fn test_value_conversion_uuid() {
530 let uuid = types::Uuid(
531 Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").expect("failed to parse uuid"),
532 );
533 let value: Value = uuid.clone().into();
534 assert_eq!(value.as_uuid(), Some(&uuid));
535 }
536
537 #[test]
538 fn test_value_type_name() {
539 let int_value: Value = types::Int32(42).into();
540 assert_eq!(int_value.type_name(), "Int32");
541
542 let text_value: Value = types::Text("Hello".to_string()).into();
543 assert_eq!(text_value.type_name(), "Text");
544
545 let null_value = Value::Null;
546 assert_eq!(null_value.type_name(), "Null");
547 }
548
549 #[test]
550 fn test_value_from_str() {
551 let str_value = "Hello, DBMS!";
552
553 let value = Value::from_str(str_value).unwrap();
554 assert_eq!(value.as_text().unwrap().0, str_value);
555 }
556
557 #[test]
558 fn test_should_create_custom_value() {
559 let cv = crate::dbms::custom_value::CustomValue {
560 type_tag: "role".to_string(),
561 encoded: vec![0x01],
562 display: "Admin".to_string(),
563 };
564 let value = Value::Custom(cv.clone());
565 assert_eq!(value.as_custom(), Some(&cv));
566 }
567
568 #[test]
569 fn test_should_return_none_for_non_custom() {
570 let value = Value::Null;
571 assert_eq!(value.as_custom(), None);
572 }
573
574 #[test]
575 fn test_should_compare_custom_values() {
576 let a = Value::Custom(crate::dbms::custom_value::CustomValue {
577 type_tag: "role".to_string(),
578 encoded: vec![0x01],
579 display: "Admin".to_string(),
580 });
581 let b = Value::Custom(crate::dbms::custom_value::CustomValue {
582 type_tag: "role".to_string(),
583 encoded: vec![0x01],
584 display: "Admin".to_string(),
585 });
586 assert_eq!(a, b);
587 }
588
589 #[test]
590 fn test_should_order_custom_after_builtin() {
591 let builtin = Value::Uuid(types::Uuid::default());
592 let custom = Value::Custom(crate::dbms::custom_value::CustomValue {
593 type_tag: "role".to_string(),
594 encoded: vec![0x01],
595 display: "Admin".to_string(),
596 });
597 assert!(builtin < custom);
598 }
599
600 #[test]
601 fn test_should_get_custom_type_name() {
602 let cv = Value::Custom(crate::dbms::custom_value::CustomValue {
603 type_tag: "role".to_string(),
604 encoded: vec![0x01],
605 display: "Admin".to_string(),
606 });
607 assert_eq!(cv.type_name(), "Custom(role)");
608 }
609
610 #[test]
613 fn test_encode_decode_null() {
614 let original = Value::Null;
615 let encoded = Encode::encode(&original);
616 assert_eq!(encoded.len(), 1);
617 let decoded = Value::decode(encoded).unwrap();
618 assert_eq!(original, decoded);
619 }
620
621 #[test]
622 fn test_encode_decode_uint32() {
623 let original = Value::Uint32(types::Uint32(42));
624 let encoded = Encode::encode(&original);
625 let decoded = Value::decode(encoded).unwrap();
626 assert_eq!(original, decoded);
627 }
628
629 #[test]
630 fn test_encode_decode_text() {
631 let original = Value::Text(types::Text("hello index".to_string()));
632 let encoded = Encode::encode(&original);
633 let decoded = Value::decode(encoded).unwrap();
634 assert_eq!(original, decoded);
635 }
636
637 #[test]
638 fn test_encode_decode_blob() {
639 let original = Value::Blob(types::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF]));
640 let encoded = Encode::encode(&original);
641 let decoded = Value::decode(encoded).unwrap();
642 assert_eq!(original, decoded);
643 }
644
645 #[test]
646 fn test_encode_decode_boolean() {
647 let original = Value::Boolean(types::Boolean(true));
648 let encoded = Encode::encode(&original);
649 let decoded = Value::decode(encoded).unwrap();
650 assert_eq!(original, decoded);
651 }
652
653 #[test]
654 fn test_encode_decode_date() {
655 let original = Value::Date(types::Date {
656 year: 2026,
657 month: 3,
658 day: 29,
659 });
660 let encoded = Encode::encode(&original);
661 let decoded = Value::decode(encoded).unwrap();
662 assert_eq!(original, decoded);
663 }
664
665 #[test]
666 fn test_encode_decode_datetime() {
667 let original = Value::DateTime(types::DateTime {
668 year: 2026,
669 month: 3,
670 day: 29,
671 hour: 14,
672 minute: 30,
673 second: 0,
674 microsecond: 0,
675 timezone_offset_minutes: 60,
676 });
677 let encoded = Encode::encode(&original);
678 let decoded = Value::decode(encoded).unwrap();
679 assert_eq!(original, decoded);
680 }
681
682 #[test]
683 fn test_encode_decode_decimal() {
684 let original = Value::Decimal(types::Decimal(rust_decimal::Decimal::new(12345, 2)));
685 let encoded = Encode::encode(&original);
686 let decoded = Value::decode(encoded).unwrap();
687 assert_eq!(original, decoded);
688 }
689
690 #[test]
691 fn test_encode_decode_int8() {
692 let original = Value::Int8(types::Int8(-42));
693 let encoded = Encode::encode(&original);
694 let decoded = Value::decode(encoded).unwrap();
695 assert_eq!(original, decoded);
696 }
697
698 #[test]
699 fn test_encode_decode_int16() {
700 let original = Value::Int16(types::Int16(-1000));
701 let encoded = Encode::encode(&original);
702 let decoded = Value::decode(encoded).unwrap();
703 assert_eq!(original, decoded);
704 }
705
706 #[test]
707 fn test_encode_decode_int32() {
708 let original = Value::Int32(types::Int32(-100_000));
709 let encoded = Encode::encode(&original);
710 let decoded = Value::decode(encoded).unwrap();
711 assert_eq!(original, decoded);
712 }
713
714 #[test]
715 fn test_encode_decode_int64() {
716 let original = Value::Int64(types::Int64(-9_000_000_000));
717 let encoded = Encode::encode(&original);
718 let decoded = Value::decode(encoded).unwrap();
719 assert_eq!(original, decoded);
720 }
721
722 #[test]
723 fn test_encode_decode_uint8() {
724 let original = Value::Uint8(types::Uint8(255));
725 let encoded = Encode::encode(&original);
726 let decoded = Value::decode(encoded).unwrap();
727 assert_eq!(original, decoded);
728 }
729
730 #[test]
731 fn test_encode_decode_uint16() {
732 let original = Value::Uint16(types::Uint16(60_000));
733 let encoded = Encode::encode(&original);
734 let decoded = Value::decode(encoded).unwrap();
735 assert_eq!(original, decoded);
736 }
737
738 #[test]
739 fn test_encode_decode_uint64() {
740 let original = Value::Uint64(types::Uint64(18_446_744_073_709_551_615));
741 let encoded = Encode::encode(&original);
742 let decoded = Value::decode(encoded).unwrap();
743 assert_eq!(original, decoded);
744 }
745
746 #[test]
747 fn test_encode_decode_uuid() {
748 let original = Value::Uuid(types::Uuid(
749 Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap(),
750 ));
751 let encoded = Encode::encode(&original);
752 let decoded = Value::decode(encoded).unwrap();
753 assert_eq!(original, decoded);
754 }
755
756 #[test]
757 fn test_encode_decode_custom() {
758 let original = Value::Custom(crate::dbms::custom_value::CustomValue {
759 type_tag: "role".to_string(),
760 encoded: vec![0x01, 0x02],
761 display: "Admin".to_string(),
762 });
763 let encoded = Encode::encode(&original);
764 let decoded = Value::decode(encoded).unwrap();
765 assert_eq!(decoded.as_custom().unwrap().type_tag, "role");
767 assert_eq!(decoded.as_custom().unwrap().encoded, vec![0x01, 0x02]);
768 }
769
770 #[test]
771 fn test_encode_decode_invalid_discriminant() {
772 let data = Cow::Owned(vec![0xFF]);
773 let result = Value::decode(data);
774 assert!(result.is_err());
775 }
776
777 #[test]
778 fn test_encode_decode_empty_data() {
779 let data: Cow<[u8]> = Cow::Owned(vec![]);
780 let result = Value::decode(data);
781 assert!(result.is_err());
782 }
783
784 #[test]
785 fn test_value_size_matches_encoded_length() {
786 let values = vec![
787 Value::Null,
788 Value::Uint32(types::Uint32(42)),
789 Value::Text(types::Text("test".to_string())),
790 Value::Boolean(types::Boolean(false)),
791 ];
792 for value in &values {
793 let encoded = Encode::encode(value);
794 assert_eq!(
795 Encode::size(value) as usize,
796 encoded.len(),
797 "size mismatch for {value:?}"
798 );
799 }
800 }
801
802 #[test]
805 fn test_encode_decode_vec_single_value() {
806 let original = vec![Value::Uint32(types::Uint32(99))];
807 let encoded = Encode::encode(&original);
808 let decoded = Vec::<Value>::decode(encoded).unwrap();
809 assert_eq!(original, decoded);
810 }
811
812 #[test]
813 fn test_encode_decode_vec_composite() {
814 let original = vec![
815 Value::Text(types::Text("alice".to_string())),
816 Value::Uint32(types::Uint32(30)),
817 ];
818 let encoded = Encode::encode(&original);
819 let decoded = Vec::<Value>::decode(encoded).unwrap();
820 assert_eq!(original, decoded);
821 }
822
823 #[test]
824 fn test_encode_decode_vec_empty() {
825 let original: Vec<Value> = vec![];
826 let encoded = Encode::encode(&original);
827 let decoded = Vec::<Value>::decode(encoded).unwrap();
828 assert_eq!(original, decoded);
829 }
830
831 #[test]
832 fn test_vec_value_size_matches_encoded_length() {
833 let original = vec![
834 Value::Text(types::Text("hello".to_string())),
835 Value::Null,
836 Value::Int64(types::Int64(-1)),
837 ];
838 let encoded = Encode::encode(&original);
839 assert_eq!(Encode::size(&original) as usize, encoded.len());
840 }
841
842 #[test]
843 fn test_vec_value_decode_bounds_declared_count_by_input() {
844 let bytes = 1_000_000u32.to_le_bytes();
846
847 let (result, largest) = crate::test_alloc::largest_allocation_during(|| {
848 Vec::<Value>::decode(Cow::Borrowed(&bytes))
849 });
850
851 assert!(
852 matches!(result, Err(MemoryError::DecodeError(DecodeError::TooShort))),
853 "unexpected decode result: {result:?}"
854 );
855 assert!(
857 largest < 1_024,
858 "decoder allocated {largest} bytes for a {}-byte input",
859 bytes.len()
860 );
861 }
862
863 #[test]
864 fn test_vec_value_decode_bounds_count_with_partial_input() {
865 let mut bytes = u32::MAX.to_le_bytes().to_vec();
867 bytes.extend_from_slice(&1u32.to_le_bytes());
868 bytes.extend_from_slice(&Encode::encode(&Value::Null));
869
870 let (result, largest) = crate::test_alloc::largest_allocation_during(|| {
871 Vec::<Value>::decode(Cow::Borrowed(&bytes))
872 });
873
874 assert!(
875 matches!(result, Err(MemoryError::DecodeError(DecodeError::TooShort))),
876 "unexpected decode result: {result:?}"
877 );
878 assert!(
880 largest < 1_024,
881 "decoder allocated {largest} bytes for a {}-byte input",
882 bytes.len()
883 );
884 }
885
886 fn custom_value(encoded_len: usize) -> Value {
889 Value::Custom(crate::dbms::custom_value::CustomValue {
890 type_tag: "t".to_string(),
891 encoded: vec![0xCD; encoded_len],
892 display: String::new(),
893 })
894 }
895
896 #[test]
897 fn test_value_round_trips_at_largest_encodable_length() {
898 let text = Value::Text(types::Text("x".repeat(65_532)));
900 let blob = Value::Blob(types::Blob(vec![0xAB; 65_532]));
902 let custom = custom_value(65_529);
904
905 for value in [text, blob, custom] {
906 assert_eq!(Encode::size(&value), MSize::MAX);
907 let encoded = Encode::encode(&value);
908 assert_eq!(encoded.len(), MSize::MAX as usize);
909 let decoded = Value::decode(encoded).unwrap();
910 assert_eq!(decoded, value);
911 }
912 }
913
914 #[test]
915 fn test_value_size_saturates_when_payload_does_not_fit() {
916 let values = [
917 Value::Text(types::Text("x".repeat(65_533))),
918 Value::Text(types::Text("x".repeat(65_536))),
919 Value::Blob(types::Blob(vec![0xAB; 65_533])),
920 Value::Blob(types::Blob(vec![0xAB; 65_536])),
921 Value::Json(types::Json::from(serde_json::json!("x".repeat(65_534)))),
922 custom_value(65_530),
923 custom_value(65_536),
924 custom_value(131_072),
925 ];
926 for value in &values {
927 assert_eq!(
928 Encode::size(value),
929 MSize::MAX,
930 "size must saturate for {}",
931 value.type_name()
932 );
933 }
934 }
935
936 #[test]
937 fn test_vec_value_size_saturates_when_payload_does_not_fit() {
938 let original = vec![
940 Value::Text(types::Text("x".repeat(40_000))),
941 Value::Text(types::Text("y".repeat(40_000))),
942 ];
943 assert_eq!(Encode::size(&original), MSize::MAX);
944 }
945}