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