Skip to main content

icydb_core/db/data/persisted_row/
codec.rs

1//! Module: db::data::persisted_row::codec
2//! Defines the persisted scalar row-slot encoding and borrowed decoding helpers
3//! used by runtime row access.
4
5use crate::{
6    db::data::{
7        decode_storage_key_binary_value_bytes, decode_structural_field_by_kind_bytes,
8        decode_structural_value_storage_bytes, encode_storage_key_binary_value_bytes,
9        encode_structural_field_by_kind_bytes, encode_structural_value_storage_bytes,
10        supports_storage_key_binary_kind,
11    },
12    error::InternalError,
13    model::field::{FieldKind, ScalarCodec},
14    traits::{FieldTypeMeta, FieldValue, field_value_vec_from_value},
15    types::{Blob, Date, Duration, Float32, Float64, Principal, Subaccount, Timestamp, Ulid, Unit},
16    value::Value,
17};
18use std::str;
19
20const SCALAR_SLOT_PREFIX: u8 = 0xFF;
21const SCALAR_SLOT_TAG_NULL: u8 = 0;
22const SCALAR_SLOT_TAG_VALUE: u8 = 1;
23
24const SCALAR_BOOL_PAYLOAD_LEN: usize = 1;
25const SCALAR_WORD32_PAYLOAD_LEN: usize = 4;
26const SCALAR_WORD64_PAYLOAD_LEN: usize = 8;
27const SCALAR_ULID_PAYLOAD_LEN: usize = 16;
28const SCALAR_SUBACCOUNT_PAYLOAD_LEN: usize = 32;
29
30const SCALAR_BOOL_FALSE_TAG: u8 = 0;
31const SCALAR_BOOL_TRUE_TAG: u8 = 1;
32
33///
34/// ScalarValueRef
35///
36/// ScalarValueRef is the borrowed-or-copy scalar payload view returned by the
37/// slot-reader fast path.
38/// It preserves cheap references for text/blob payloads while keeping fixed
39/// width scalar wrappers as copy values.
40///
41
42#[derive(Clone, Copy, Debug)]
43pub enum ScalarValueRef<'a> {
44    Blob(&'a [u8]),
45    Bool(bool),
46    Date(Date),
47    Duration(Duration),
48    Float32(Float32),
49    Float64(Float64),
50    Int(i64),
51    Principal(Principal),
52    Subaccount(Subaccount),
53    Text(&'a str),
54    Timestamp(Timestamp),
55    Uint(u64),
56    Ulid(Ulid),
57    Unit,
58}
59
60impl ScalarValueRef<'_> {
61    /// Materialize this scalar view into the runtime `Value` enum.
62    #[must_use]
63    pub fn into_value(self) -> Value {
64        match self {
65            Self::Blob(value) => Value::Blob(value.to_vec()),
66            Self::Bool(value) => Value::Bool(value),
67            Self::Date(value) => Value::Date(value),
68            Self::Duration(value) => Value::Duration(value),
69            Self::Float32(value) => Value::Float32(value),
70            Self::Float64(value) => Value::Float64(value),
71            Self::Int(value) => Value::Int(value),
72            Self::Principal(value) => Value::Principal(value),
73            Self::Subaccount(value) => Value::Subaccount(value),
74            Self::Text(value) => Value::Text(value.to_owned()),
75            Self::Timestamp(value) => Value::Timestamp(value),
76            Self::Uint(value) => Value::Uint(value),
77            Self::Ulid(value) => Value::Ulid(value),
78            Self::Unit => Value::Unit,
79        }
80    }
81}
82
83///
84/// ScalarSlotValueRef
85///
86/// ScalarSlotValueRef preserves the distinction between a missing slot and an
87/// explicitly persisted `NULL` scalar payload.
88/// The outer `Option` from `SlotReader::get_scalar` therefore still means
89/// "slot absent".
90///
91
92#[derive(Clone, Copy, Debug)]
93pub enum ScalarSlotValueRef<'a> {
94    Null,
95    Value(ScalarValueRef<'a>),
96}
97
98///
99/// PersistedScalar
100///
101/// PersistedScalar defines the canonical binary payload codec for one scalar
102/// leaf type.
103/// Derive-generated persisted-row materializers and writers use this trait to
104/// avoid routing scalar fields back through a generic structural envelope.
105///
106
107pub trait PersistedScalar: Sized {
108    /// Canonical scalar codec identifier used by schema/runtime metadata.
109    const CODEC: ScalarCodec;
110
111    /// Encode this scalar value into its codec-specific payload bytes.
112    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError>;
113
114    /// Decode this scalar value from its codec-specific payload bytes.
115    fn decode_scalar_payload(bytes: &[u8], field_name: &'static str)
116    -> Result<Self, InternalError>;
117}
118
119/// Encode one persisted slot payload using the stricter schema-owned `ByKind`
120/// storage contract.
121pub fn encode_persisted_slot_payload_by_kind<T>(
122    value: &T,
123    kind: FieldKind,
124    field_name: &'static str,
125) -> Result<Vec<u8>, InternalError>
126where
127    T: FieldValue,
128{
129    if supports_storage_key_binary_kind(kind) {
130        return encode_storage_key_binary_value_bytes(kind, &value.to_value(), field_name)?
131            .ok_or_else(|| {
132                InternalError::persisted_row_field_encode_failed(
133                    field_name,
134                    "storage-key binary lane rejected a supported field kind",
135                )
136            });
137    }
138
139    encode_structural_field_by_kind_bytes(kind, &value.to_value(), field_name)
140        .map_err(|err| InternalError::persisted_row_field_encode_failed(field_name, err))
141}
142
143/// Encode one persisted scalar slot payload using the canonical scalar envelope.
144pub fn encode_persisted_scalar_slot_payload<T>(
145    value: &T,
146    field_name: &'static str,
147) -> Result<Vec<u8>, InternalError>
148where
149    T: PersistedScalar,
150{
151    let payload = value.encode_scalar_payload()?;
152    let mut encoded = Vec::with_capacity(payload.len() + 2);
153    encoded.push(SCALAR_SLOT_PREFIX);
154    encoded.push(SCALAR_SLOT_TAG_VALUE);
155    encoded.extend_from_slice(&payload);
156
157    if encoded.len() < 2 {
158        return Err(InternalError::persisted_row_field_encode_failed(
159            field_name,
160            "scalar payload envelope underflow",
161        ));
162    }
163
164    Ok(encoded)
165}
166
167/// Encode one optional persisted scalar slot payload preserving explicit `NULL`.
168pub fn encode_persisted_option_scalar_slot_payload<T>(
169    value: &Option<T>,
170    field_name: &'static str,
171) -> Result<Vec<u8>, InternalError>
172where
173    T: PersistedScalar,
174{
175    match value {
176        Some(value) => encode_persisted_scalar_slot_payload(value, field_name),
177        None => Ok(vec![SCALAR_SLOT_PREFIX, SCALAR_SLOT_TAG_NULL]),
178    }
179}
180
181// Decode one `ByKind` structural persisted payload, preserving the explicit
182// null sentinel instead of forcing each wrapper to repeat the same branch.
183fn decode_persisted_structural_slot_payload_by_kind<T>(
184    bytes: &[u8],
185    kind: FieldKind,
186    field_name: &'static str,
187) -> Result<Option<T>, InternalError>
188where
189    T: FieldValue,
190{
191    let value = if supports_storage_key_binary_kind(kind) {
192        decode_storage_key_binary_value_bytes(bytes, kind)
193            .map_err(|err| InternalError::persisted_row_field_decode_failed(field_name, err))?
194            .ok_or_else(|| {
195                InternalError::persisted_row_field_decode_failed(
196                    field_name,
197                    "storage-key binary lane rejected a supported field kind",
198                )
199            })?
200    } else {
201        decode_structural_field_by_kind_bytes(bytes, kind)
202            .map_err(|err| InternalError::persisted_row_field_decode_failed(field_name, err))?
203    };
204
205    if matches!(value, Value::Null) {
206        return Ok(None);
207    }
208
209    T::from_value(&value).map(Some).ok_or_else(|| {
210        InternalError::persisted_row_field_decode_failed(
211            field_name,
212            format!(
213                "value payload does not match {}",
214                std::any::type_name::<T>()
215            ),
216        )
217    })
218}
219
220/// Decode one persisted slot payload using the stricter schema-owned `ByKind`
221/// storage contract.
222pub fn decode_persisted_slot_payload_by_kind<T>(
223    bytes: &[u8],
224    kind: FieldKind,
225    field_name: &'static str,
226) -> Result<T, InternalError>
227where
228    T: FieldValue,
229{
230    let value = if supports_storage_key_binary_kind(kind) {
231        decode_storage_key_binary_value_bytes(bytes, kind)
232            .map_err(|err| InternalError::persisted_row_field_decode_failed(field_name, err))?
233            .ok_or_else(|| {
234                InternalError::persisted_row_field_decode_failed(
235                    field_name,
236                    "storage-key binary lane rejected a supported field kind",
237                )
238            })?
239    } else {
240        decode_structural_field_by_kind_bytes(bytes, kind)
241            .map_err(|err| InternalError::persisted_row_field_decode_failed(field_name, err))?
242    };
243
244    T::from_value(&value).ok_or_else(|| {
245        InternalError::persisted_row_field_decode_failed(
246            field_name,
247            format!(
248                "value payload does not match {}",
249                std::any::type_name::<T>()
250            ),
251        )
252    })
253}
254
255/// Decode one non-null persisted slot payload through the stricter schema-owned
256/// `ByKind` storage contract.
257pub fn decode_persisted_non_null_slot_payload_by_kind<T>(
258    bytes: &[u8],
259    kind: FieldKind,
260    field_name: &'static str,
261) -> Result<T, InternalError>
262where
263    T: FieldValue,
264{
265    decode_persisted_structural_slot_payload_by_kind(bytes, kind, field_name)?.ok_or_else(|| {
266        InternalError::persisted_row_field_decode_failed(
267            field_name,
268            "unexpected null for non-nullable field",
269        )
270    })
271}
272
273/// Decode one optional persisted slot payload preserving the explicit null
274/// sentinel under the stricter schema-owned `ByKind` storage contract.
275pub fn decode_persisted_option_slot_payload_by_kind<T>(
276    bytes: &[u8],
277    kind: FieldKind,
278    field_name: &'static str,
279) -> Result<Option<T>, InternalError>
280where
281    T: FieldValue,
282{
283    decode_persisted_structural_slot_payload_by_kind(bytes, kind, field_name)
284}
285
286/// Decode one persisted slot payload using the field type's own runtime field
287/// metadata.
288pub fn decode_persisted_slot_payload_by_meta<T>(
289    bytes: &[u8],
290    field_name: &'static str,
291) -> Result<T, InternalError>
292where
293    T: FieldTypeMeta + FieldValue,
294{
295    match T::STORAGE_DECODE {
296        crate::model::field::FieldStorageDecode::ByKind => {
297            decode_persisted_non_null_slot_payload_by_kind(bytes, T::KIND, field_name)
298        }
299        crate::model::field::FieldStorageDecode::Value => {
300            decode_persisted_custom_slot_payload(bytes, field_name)
301        }
302    }
303}
304
305/// Decode one optional persisted slot payload using the inner field type's own
306/// runtime field metadata.
307pub fn decode_persisted_option_slot_payload_by_meta<T>(
308    bytes: &[u8],
309    field_name: &'static str,
310) -> Result<Option<T>, InternalError>
311where
312    T: FieldTypeMeta + FieldValue,
313{
314    match T::STORAGE_DECODE {
315        crate::model::field::FieldStorageDecode::ByKind => {
316            decode_persisted_option_slot_payload_by_kind(bytes, T::KIND, field_name)
317        }
318        crate::model::field::FieldStorageDecode::Value => {
319            decode_persisted_custom_slot_payload(bytes, field_name)
320        }
321    }
322}
323
324// Decode one persisted `Value` payload, then let a narrow owner-local
325// conversion closure rebuild the typed field contract.
326fn decode_persisted_value_payload_as<T, F>(
327    bytes: &[u8],
328    field_name: &'static str,
329    mismatch: impl FnOnce() -> String,
330    decode: F,
331) -> Result<T, InternalError>
332where
333    F: FnOnce(&Value) -> Option<T>,
334{
335    let value = decode_structural_value_storage_bytes(bytes)
336        .map_err(|err| InternalError::persisted_row_field_decode_failed(field_name, err))?;
337
338    decode(&value)
339        .ok_or_else(|| InternalError::persisted_row_field_decode_failed(field_name, mismatch()))
340}
341
342// Encode one already materialized `Value` through the shared structural leaf
343// payload seam.
344fn encode_persisted_value_payload(
345    value: Value,
346    field_name: &'static str,
347) -> Result<Vec<u8>, InternalError> {
348    encode_structural_value_storage_bytes(&value)
349        .map_err(|err| InternalError::persisted_row_field_encode_failed(field_name, err))
350}
351
352/// Decode one persisted custom-schema payload through `Value` and reconstruct
353/// the typed field via `FieldValue`.
354pub fn decode_persisted_custom_slot_payload<T>(
355    bytes: &[u8],
356    field_name: &'static str,
357) -> Result<T, InternalError>
358where
359    T: FieldValue,
360{
361    decode_persisted_value_payload_as(
362        bytes,
363        field_name,
364        || {
365            format!(
366                "value payload does not match {}",
367                std::any::type_name::<T>()
368            )
369        },
370        T::from_value,
371    )
372}
373
374/// Decode one persisted repeated custom-schema payload through `Value::List`
375/// and reconstruct each item via `FieldValue`.
376pub fn decode_persisted_custom_many_slot_payload<T>(
377    bytes: &[u8],
378    field_name: &'static str,
379) -> Result<Vec<T>, InternalError>
380where
381    T: FieldValue,
382{
383    decode_persisted_value_payload_as(
384        bytes,
385        field_name,
386        || {
387            format!(
388                "value payload does not match Vec<{}>",
389                std::any::type_name::<T>()
390            )
391        },
392        field_value_vec_from_value::<T>,
393    )
394}
395
396/// Encode one custom-schema field payload through its `FieldValue`
397/// representation so structural decode preserves nested custom values.
398pub fn encode_persisted_custom_slot_payload<T>(
399    value: &T,
400    field_name: &'static str,
401) -> Result<Vec<u8>, InternalError>
402where
403    T: FieldValue,
404{
405    encode_persisted_value_payload(value.to_value(), field_name)
406}
407
408/// Encode one repeated custom-schema payload through `Value::List`.
409pub fn encode_persisted_custom_many_slot_payload<T>(
410    values: &[T],
411    field_name: &'static str,
412) -> Result<Vec<u8>, InternalError>
413where
414    T: FieldValue,
415{
416    encode_persisted_value_payload(
417        Value::List(values.iter().map(FieldValue::to_value).collect()),
418        field_name,
419    )
420}
421
422/// Encode one persisted slot payload using the field type's own runtime field
423/// metadata.
424pub fn encode_persisted_slot_payload_by_meta<T>(
425    value: &T,
426    field_name: &'static str,
427) -> Result<Vec<u8>, InternalError>
428where
429    T: FieldTypeMeta + FieldValue,
430{
431    match T::STORAGE_DECODE {
432        crate::model::field::FieldStorageDecode::ByKind => {
433            encode_persisted_slot_payload_by_kind(value, T::KIND, field_name)
434        }
435        crate::model::field::FieldStorageDecode::Value => {
436            encode_persisted_custom_slot_payload(value, field_name)
437        }
438    }
439}
440
441/// Encode one optional persisted slot payload using the inner field type's own
442/// runtime field metadata.
443pub fn encode_persisted_option_slot_payload_by_meta<T>(
444    value: &Option<T>,
445    field_name: &'static str,
446) -> Result<Vec<u8>, InternalError>
447where
448    T: FieldTypeMeta + FieldValue,
449{
450    match T::STORAGE_DECODE {
451        crate::model::field::FieldStorageDecode::ByKind => {
452            encode_persisted_slot_payload_by_kind(value, T::KIND, field_name)
453        }
454        crate::model::field::FieldStorageDecode::Value => {
455            encode_persisted_custom_slot_payload(value, field_name)
456        }
457    }
458}
459
460/// Decode one persisted scalar slot payload using the canonical scalar envelope.
461pub fn decode_persisted_scalar_slot_payload<T>(
462    bytes: &[u8],
463    field_name: &'static str,
464) -> Result<T, InternalError>
465where
466    T: PersistedScalar,
467{
468    let payload = decode_scalar_slot_payload_body(bytes, field_name)?.ok_or_else(|| {
469        InternalError::persisted_row_field_decode_failed(
470            field_name,
471            "unexpected null for non-nullable scalar field",
472        )
473    })?;
474
475    T::decode_scalar_payload(payload, field_name)
476}
477
478/// Decode one optional persisted scalar slot payload preserving explicit `NULL`.
479pub fn decode_persisted_option_scalar_slot_payload<T>(
480    bytes: &[u8],
481    field_name: &'static str,
482) -> Result<Option<T>, InternalError>
483where
484    T: PersistedScalar,
485{
486    let Some(payload) = decode_scalar_slot_payload_body(bytes, field_name)? else {
487        return Ok(None);
488    };
489
490    T::decode_scalar_payload(payload, field_name).map(Some)
491}
492
493// Encode one scalar slot value into the canonical prefixed scalar envelope.
494pub(super) fn encode_scalar_slot_value(value: ScalarSlotValueRef<'_>) -> Vec<u8> {
495    match value {
496        ScalarSlotValueRef::Null => vec![SCALAR_SLOT_PREFIX, SCALAR_SLOT_TAG_NULL],
497        ScalarSlotValueRef::Value(value) => {
498            let mut encoded = Vec::new();
499            encoded.push(SCALAR_SLOT_PREFIX);
500            encoded.push(SCALAR_SLOT_TAG_VALUE);
501
502            match value {
503                ScalarValueRef::Blob(bytes) => encoded.extend_from_slice(bytes),
504                ScalarValueRef::Bool(value) => encoded.push(u8::from(value)),
505                ScalarValueRef::Date(value) => {
506                    encoded.extend_from_slice(&value.as_days_since_epoch().to_le_bytes());
507                }
508                ScalarValueRef::Duration(value) => {
509                    encoded.extend_from_slice(&value.as_millis().to_le_bytes());
510                }
511                ScalarValueRef::Float32(value) => {
512                    encoded.extend_from_slice(&value.get().to_bits().to_le_bytes());
513                }
514                ScalarValueRef::Float64(value) => {
515                    encoded.extend_from_slice(&value.get().to_bits().to_le_bytes());
516                }
517                ScalarValueRef::Int(value) => encoded.extend_from_slice(&value.to_le_bytes()),
518                ScalarValueRef::Principal(value) => encoded.extend_from_slice(value.as_slice()),
519                ScalarValueRef::Subaccount(value) => encoded.extend_from_slice(&value.to_bytes()),
520                ScalarValueRef::Text(value) => encoded.extend_from_slice(value.as_bytes()),
521                ScalarValueRef::Timestamp(value) => {
522                    encoded.extend_from_slice(&value.as_millis().to_le_bytes());
523                }
524                ScalarValueRef::Uint(value) => encoded.extend_from_slice(&value.to_le_bytes()),
525                ScalarValueRef::Ulid(value) => encoded.extend_from_slice(&value.to_bytes()),
526                ScalarValueRef::Unit => {}
527            }
528
529            encoded
530        }
531    }
532}
533
534// Split one scalar slot envelope into `NULL` vs payload bytes.
535fn decode_scalar_slot_payload_body<'a>(
536    bytes: &'a [u8],
537    field_name: &'static str,
538) -> Result<Option<&'a [u8]>, InternalError> {
539    let Some((&prefix, rest)) = bytes.split_first() else {
540        return Err(InternalError::persisted_row_field_decode_failed(
541            field_name,
542            "empty scalar payload",
543        ));
544    };
545    if prefix != SCALAR_SLOT_PREFIX {
546        return Err(InternalError::persisted_row_field_decode_failed(
547            field_name,
548            format!(
549                "scalar payload prefix mismatch: expected slot envelope prefix byte 0x{SCALAR_SLOT_PREFIX:02X}, found 0x{prefix:02X}",
550            ),
551        ));
552    }
553    let Some((&tag, payload)) = rest.split_first() else {
554        return Err(InternalError::persisted_row_field_decode_failed(
555            field_name,
556            "truncated scalar payload tag",
557        ));
558    };
559
560    match tag {
561        SCALAR_SLOT_TAG_NULL => {
562            if !payload.is_empty() {
563                return Err(InternalError::persisted_row_field_decode_failed(
564                    field_name,
565                    "null scalar payload has trailing bytes",
566                ));
567            }
568
569            Ok(None)
570        }
571        SCALAR_SLOT_TAG_VALUE => Ok(Some(payload)),
572        _ => Err(InternalError::persisted_row_field_decode_failed(
573            field_name,
574            format!("invalid scalar payload tag {tag}"),
575        )),
576    }
577}
578
579// Decode one scalar slot view using the field-declared scalar codec.
580#[expect(clippy::too_many_lines)]
581pub(super) fn decode_scalar_slot_value<'a>(
582    bytes: &'a [u8],
583    codec: ScalarCodec,
584    field_name: &'static str,
585) -> Result<ScalarSlotValueRef<'a>, InternalError> {
586    let Some(payload) = decode_scalar_slot_payload_body(bytes, field_name)? else {
587        return Ok(ScalarSlotValueRef::Null);
588    };
589
590    let value = match codec {
591        ScalarCodec::Blob => ScalarValueRef::Blob(payload),
592        ScalarCodec::Bool => {
593            let [value] = payload else {
594                return Err(
595                    InternalError::persisted_row_field_payload_exact_len_required(
596                        field_name,
597                        "bool",
598                        SCALAR_BOOL_PAYLOAD_LEN,
599                    ),
600                );
601            };
602            match *value {
603                SCALAR_BOOL_FALSE_TAG => ScalarValueRef::Bool(false),
604                SCALAR_BOOL_TRUE_TAG => ScalarValueRef::Bool(true),
605                _ => {
606                    return Err(InternalError::persisted_row_field_payload_invalid_byte(
607                        field_name, "bool", *value,
608                    ));
609                }
610            }
611        }
612        ScalarCodec::Date => {
613            let bytes: [u8; SCALAR_WORD32_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
614                InternalError::persisted_row_field_payload_exact_len_required(
615                    field_name,
616                    "date",
617                    SCALAR_WORD32_PAYLOAD_LEN,
618                )
619            })?;
620            ScalarValueRef::Date(Date::from_days_since_epoch(i32::from_le_bytes(bytes)))
621        }
622        ScalarCodec::Duration => {
623            let bytes: [u8; SCALAR_WORD64_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
624                InternalError::persisted_row_field_payload_exact_len_required(
625                    field_name,
626                    "duration",
627                    SCALAR_WORD64_PAYLOAD_LEN,
628                )
629            })?;
630            ScalarValueRef::Duration(Duration::from_millis(u64::from_le_bytes(bytes)))
631        }
632        ScalarCodec::Float32 => {
633            let bytes: [u8; SCALAR_WORD32_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
634                InternalError::persisted_row_field_payload_exact_len_required(
635                    field_name,
636                    "float32",
637                    SCALAR_WORD32_PAYLOAD_LEN,
638                )
639            })?;
640            let value = f32::from_bits(u32::from_le_bytes(bytes));
641            let value = Float32::try_new(value).ok_or_else(|| {
642                InternalError::persisted_row_field_payload_non_finite(field_name, "float32")
643            })?;
644            ScalarValueRef::Float32(value)
645        }
646        ScalarCodec::Float64 => {
647            let bytes: [u8; SCALAR_WORD64_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
648                InternalError::persisted_row_field_payload_exact_len_required(
649                    field_name,
650                    "float64",
651                    SCALAR_WORD64_PAYLOAD_LEN,
652                )
653            })?;
654            let value = f64::from_bits(u64::from_le_bytes(bytes));
655            let value = Float64::try_new(value).ok_or_else(|| {
656                InternalError::persisted_row_field_payload_non_finite(field_name, "float64")
657            })?;
658            ScalarValueRef::Float64(value)
659        }
660        ScalarCodec::Int64 => {
661            let bytes: [u8; SCALAR_WORD64_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
662                InternalError::persisted_row_field_payload_exact_len_required(
663                    field_name,
664                    "int",
665                    SCALAR_WORD64_PAYLOAD_LEN,
666                )
667            })?;
668            ScalarValueRef::Int(i64::from_le_bytes(bytes))
669        }
670        ScalarCodec::Principal => ScalarValueRef::Principal(
671            Principal::try_from_bytes(payload)
672                .map_err(|err| InternalError::persisted_row_field_decode_failed(field_name, err))?,
673        ),
674        ScalarCodec::Subaccount => {
675            let bytes: [u8; SCALAR_SUBACCOUNT_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
676                InternalError::persisted_row_field_payload_exact_len_required(
677                    field_name,
678                    "subaccount",
679                    SCALAR_SUBACCOUNT_PAYLOAD_LEN,
680                )
681            })?;
682            ScalarValueRef::Subaccount(Subaccount::from_array(bytes))
683        }
684        ScalarCodec::Text => {
685            let value = str::from_utf8(payload).map_err(|err| {
686                InternalError::persisted_row_field_text_payload_invalid_utf8(field_name, err)
687            })?;
688            ScalarValueRef::Text(value)
689        }
690        ScalarCodec::Timestamp => {
691            let bytes: [u8; SCALAR_WORD64_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
692                InternalError::persisted_row_field_payload_exact_len_required(
693                    field_name,
694                    "timestamp",
695                    SCALAR_WORD64_PAYLOAD_LEN,
696                )
697            })?;
698            ScalarValueRef::Timestamp(Timestamp::from_millis(i64::from_le_bytes(bytes)))
699        }
700        ScalarCodec::Uint64 => {
701            let bytes: [u8; SCALAR_WORD64_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
702                InternalError::persisted_row_field_payload_exact_len_required(
703                    field_name,
704                    "uint",
705                    SCALAR_WORD64_PAYLOAD_LEN,
706                )
707            })?;
708            ScalarValueRef::Uint(u64::from_le_bytes(bytes))
709        }
710        ScalarCodec::Ulid => {
711            let bytes: [u8; SCALAR_ULID_PAYLOAD_LEN] = payload.try_into().map_err(|_| {
712                InternalError::persisted_row_field_payload_exact_len_required(
713                    field_name,
714                    "ulid",
715                    SCALAR_ULID_PAYLOAD_LEN,
716                )
717            })?;
718            ScalarValueRef::Ulid(Ulid::from_bytes(bytes))
719        }
720        ScalarCodec::Unit => {
721            if !payload.is_empty() {
722                return Err(InternalError::persisted_row_field_payload_must_be_empty(
723                    field_name, "unit",
724                ));
725            }
726            ScalarValueRef::Unit
727        }
728    };
729
730    Ok(ScalarSlotValueRef::Value(value))
731}
732
733macro_rules! impl_persisted_scalar_signed {
734    ($($ty:ty),* $(,)?) => {
735        $(
736            impl PersistedScalar for $ty {
737                const CODEC: ScalarCodec = ScalarCodec::Int64;
738
739                fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
740                    Ok(i64::from(*self).to_le_bytes().to_vec())
741                }
742
743                fn decode_scalar_payload(
744                    bytes: &[u8],
745                    field_name: &'static str,
746                ) -> Result<Self, InternalError> {
747                    let raw: [u8; SCALAR_WORD64_PAYLOAD_LEN] = bytes.try_into().map_err(|_| {
748                        InternalError::persisted_row_field_payload_exact_len_required(
749                            field_name,
750                            "int",
751                            SCALAR_WORD64_PAYLOAD_LEN,
752                        )
753                    })?;
754                    <$ty>::try_from(i64::from_le_bytes(raw)).map_err(|_| {
755                        InternalError::persisted_row_field_payload_out_of_range(
756                            field_name,
757                            "integer",
758                        )
759                    })
760                }
761            }
762        )*
763    };
764}
765
766macro_rules! impl_persisted_scalar_unsigned {
767    ($($ty:ty),* $(,)?) => {
768        $(
769            impl PersistedScalar for $ty {
770                const CODEC: ScalarCodec = ScalarCodec::Uint64;
771
772                fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
773                    Ok(u64::from(*self).to_le_bytes().to_vec())
774                }
775
776                fn decode_scalar_payload(
777                    bytes: &[u8],
778                    field_name: &'static str,
779                ) -> Result<Self, InternalError> {
780                    let raw: [u8; SCALAR_WORD64_PAYLOAD_LEN] = bytes.try_into().map_err(|_| {
781                        InternalError::persisted_row_field_payload_exact_len_required(
782                            field_name,
783                            "uint",
784                            SCALAR_WORD64_PAYLOAD_LEN,
785                        )
786                    })?;
787                    <$ty>::try_from(u64::from_le_bytes(raw)).map_err(|_| {
788                        InternalError::persisted_row_field_payload_out_of_range(
789                            field_name,
790                            "unsigned",
791                        )
792                    })
793                }
794            }
795        )*
796    };
797}
798
799impl_persisted_scalar_signed!(i8, i16, i32, i64);
800impl_persisted_scalar_unsigned!(u8, u16, u32, u64);
801
802impl PersistedScalar for bool {
803    const CODEC: ScalarCodec = ScalarCodec::Bool;
804
805    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
806        Ok(vec![u8::from(*self)])
807    }
808
809    fn decode_scalar_payload(
810        bytes: &[u8],
811        field_name: &'static str,
812    ) -> Result<Self, InternalError> {
813        let [value] = bytes else {
814            return Err(
815                InternalError::persisted_row_field_payload_exact_len_required(
816                    field_name,
817                    "bool",
818                    SCALAR_BOOL_PAYLOAD_LEN,
819                ),
820            );
821        };
822
823        match *value {
824            SCALAR_BOOL_FALSE_TAG => Ok(false),
825            SCALAR_BOOL_TRUE_TAG => Ok(true),
826            _ => Err(InternalError::persisted_row_field_payload_invalid_byte(
827                field_name, "bool", *value,
828            )),
829        }
830    }
831}
832
833impl PersistedScalar for String {
834    const CODEC: ScalarCodec = ScalarCodec::Text;
835
836    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
837        Ok(self.as_bytes().to_vec())
838    }
839
840    fn decode_scalar_payload(
841        bytes: &[u8],
842        field_name: &'static str,
843    ) -> Result<Self, InternalError> {
844        str::from_utf8(bytes).map(str::to_owned).map_err(|err| {
845            InternalError::persisted_row_field_text_payload_invalid_utf8(field_name, err)
846        })
847    }
848}
849
850impl PersistedScalar for Vec<u8> {
851    const CODEC: ScalarCodec = ScalarCodec::Blob;
852
853    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
854        Ok(self.clone())
855    }
856
857    fn decode_scalar_payload(
858        bytes: &[u8],
859        _field_name: &'static str,
860    ) -> Result<Self, InternalError> {
861        Ok(bytes.to_vec())
862    }
863}
864
865impl PersistedScalar for Blob {
866    const CODEC: ScalarCodec = ScalarCodec::Blob;
867
868    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
869        Ok(self.to_vec())
870    }
871
872    fn decode_scalar_payload(
873        bytes: &[u8],
874        _field_name: &'static str,
875    ) -> Result<Self, InternalError> {
876        Ok(Self::from(bytes))
877    }
878}
879
880impl PersistedScalar for Ulid {
881    const CODEC: ScalarCodec = ScalarCodec::Ulid;
882
883    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
884        Ok(self.to_bytes().to_vec())
885    }
886
887    fn decode_scalar_payload(
888        bytes: &[u8],
889        field_name: &'static str,
890    ) -> Result<Self, InternalError> {
891        Self::try_from_bytes(bytes)
892            .map_err(|err| InternalError::persisted_row_field_decode_failed(field_name, err))
893    }
894}
895
896impl PersistedScalar for Timestamp {
897    const CODEC: ScalarCodec = ScalarCodec::Timestamp;
898
899    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
900        Ok(self.as_millis().to_le_bytes().to_vec())
901    }
902
903    fn decode_scalar_payload(
904        bytes: &[u8],
905        field_name: &'static str,
906    ) -> Result<Self, InternalError> {
907        let raw: [u8; SCALAR_WORD64_PAYLOAD_LEN] = bytes.try_into().map_err(|_| {
908            InternalError::persisted_row_field_payload_exact_len_required(
909                field_name,
910                "timestamp",
911                SCALAR_WORD64_PAYLOAD_LEN,
912            )
913        })?;
914
915        Ok(Self::from_millis(i64::from_le_bytes(raw)))
916    }
917}
918
919impl PersistedScalar for Date {
920    const CODEC: ScalarCodec = ScalarCodec::Date;
921
922    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
923        Ok(self.as_days_since_epoch().to_le_bytes().to_vec())
924    }
925
926    fn decode_scalar_payload(
927        bytes: &[u8],
928        field_name: &'static str,
929    ) -> Result<Self, InternalError> {
930        let raw: [u8; SCALAR_WORD32_PAYLOAD_LEN] = bytes.try_into().map_err(|_| {
931            InternalError::persisted_row_field_payload_exact_len_required(
932                field_name,
933                "date",
934                SCALAR_WORD32_PAYLOAD_LEN,
935            )
936        })?;
937
938        Ok(Self::from_days_since_epoch(i32::from_le_bytes(raw)))
939    }
940}
941
942impl PersistedScalar for Duration {
943    const CODEC: ScalarCodec = ScalarCodec::Duration;
944
945    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
946        Ok(self.as_millis().to_le_bytes().to_vec())
947    }
948
949    fn decode_scalar_payload(
950        bytes: &[u8],
951        field_name: &'static str,
952    ) -> Result<Self, InternalError> {
953        let raw: [u8; SCALAR_WORD64_PAYLOAD_LEN] = bytes.try_into().map_err(|_| {
954            InternalError::persisted_row_field_payload_exact_len_required(
955                field_name,
956                "duration",
957                SCALAR_WORD64_PAYLOAD_LEN,
958            )
959        })?;
960
961        Ok(Self::from_millis(u64::from_le_bytes(raw)))
962    }
963}
964
965impl PersistedScalar for Float32 {
966    const CODEC: ScalarCodec = ScalarCodec::Float32;
967
968    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
969        Ok(self.get().to_bits().to_le_bytes().to_vec())
970    }
971
972    fn decode_scalar_payload(
973        bytes: &[u8],
974        field_name: &'static str,
975    ) -> Result<Self, InternalError> {
976        let raw: [u8; SCALAR_WORD32_PAYLOAD_LEN] = bytes.try_into().map_err(|_| {
977            InternalError::persisted_row_field_payload_exact_len_required(
978                field_name,
979                "float32",
980                SCALAR_WORD32_PAYLOAD_LEN,
981            )
982        })?;
983        let value = f32::from_bits(u32::from_le_bytes(raw));
984
985        Self::try_new(value).ok_or_else(|| {
986            InternalError::persisted_row_field_payload_non_finite(field_name, "float32")
987        })
988    }
989}
990
991impl PersistedScalar for Float64 {
992    const CODEC: ScalarCodec = ScalarCodec::Float64;
993
994    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
995        Ok(self.get().to_bits().to_le_bytes().to_vec())
996    }
997
998    fn decode_scalar_payload(
999        bytes: &[u8],
1000        field_name: &'static str,
1001    ) -> Result<Self, InternalError> {
1002        let raw: [u8; SCALAR_WORD64_PAYLOAD_LEN] = bytes.try_into().map_err(|_| {
1003            InternalError::persisted_row_field_payload_exact_len_required(
1004                field_name,
1005                "float64",
1006                SCALAR_WORD64_PAYLOAD_LEN,
1007            )
1008        })?;
1009        let value = f64::from_bits(u64::from_le_bytes(raw));
1010
1011        Self::try_new(value).ok_or_else(|| {
1012            InternalError::persisted_row_field_payload_non_finite(field_name, "float64")
1013        })
1014    }
1015}
1016
1017impl PersistedScalar for Principal {
1018    const CODEC: ScalarCodec = ScalarCodec::Principal;
1019
1020    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
1021        self.to_bytes()
1022            .map_err(|err| InternalError::persisted_row_field_encode_failed("principal", err))
1023    }
1024
1025    fn decode_scalar_payload(
1026        bytes: &[u8],
1027        field_name: &'static str,
1028    ) -> Result<Self, InternalError> {
1029        Self::try_from_bytes(bytes)
1030            .map_err(|err| InternalError::persisted_row_field_decode_failed(field_name, err))
1031    }
1032}
1033
1034impl PersistedScalar for Subaccount {
1035    const CODEC: ScalarCodec = ScalarCodec::Subaccount;
1036
1037    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
1038        Ok(self.to_bytes().to_vec())
1039    }
1040
1041    fn decode_scalar_payload(
1042        bytes: &[u8],
1043        field_name: &'static str,
1044    ) -> Result<Self, InternalError> {
1045        let raw: [u8; SCALAR_SUBACCOUNT_PAYLOAD_LEN] = bytes.try_into().map_err(|_| {
1046            InternalError::persisted_row_field_payload_exact_len_required(
1047                field_name,
1048                "subaccount",
1049                SCALAR_SUBACCOUNT_PAYLOAD_LEN,
1050            )
1051        })?;
1052
1053        Ok(Self::from_array(raw))
1054    }
1055}
1056
1057impl PersistedScalar for () {
1058    const CODEC: ScalarCodec = ScalarCodec::Unit;
1059
1060    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
1061        Ok(Vec::new())
1062    }
1063
1064    fn decode_scalar_payload(
1065        bytes: &[u8],
1066        field_name: &'static str,
1067    ) -> Result<Self, InternalError> {
1068        if !bytes.is_empty() {
1069            return Err(InternalError::persisted_row_field_payload_must_be_empty(
1070                field_name, "unit",
1071            ));
1072        }
1073
1074        Ok(())
1075    }
1076}
1077
1078impl PersistedScalar for Unit {
1079    const CODEC: ScalarCodec = ScalarCodec::Unit;
1080
1081    fn encode_scalar_payload(&self) -> Result<Vec<u8>, InternalError> {
1082        Ok(Vec::new())
1083    }
1084
1085    fn decode_scalar_payload(
1086        bytes: &[u8],
1087        field_name: &'static str,
1088    ) -> Result<Self, InternalError> {
1089        if !bytes.is_empty() {
1090            return Err(InternalError::persisted_row_field_payload_must_be_empty(
1091                field_name, "unit",
1092            ));
1093        }
1094
1095        Ok(Self)
1096    }
1097}