Skip to main content

lora_store/
codec.rs

1//! Dependency-free binary codecs for store-owned values that must cross
2//! crate boundaries.
3//!
4//! `lora-wal` and `lora-snapshot` own their container formats, but the
5//! byte shape for core store types belongs here so catalog DDL, property
6//! values, and snapshot index metadata do not drift apart.
7
8use std::collections::BTreeMap;
9use std::fmt;
10
11use crate::{
12    ConstraintDefinition, ConstraintRequest, IndexConfigValue, IndexDefinition, IndexRequest,
13    LoraBinary, LoraDate, LoraDateTime, LoraDuration, LoraLocalDateTime, LoraLocalTime, LoraPoint,
14    LoraTime, LoraVector, PropertyValue, StoredConstraintKind, StoredIndexEntity, StoredIndexKind,
15    StoredIndexState, StoredPropertyType, StoredPropertyTypeTerm, StoredScalarType,
16    StoredVectorCoordType, VectorValues,
17};
18
19type Result<T> = std::result::Result<T, StoreCodecError>;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum StoreCodecError {
23    Encode(String),
24    Decode(String),
25}
26
27impl fmt::Display for StoreCodecError {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Self::Encode(message) => write!(f, "store value encode failed: {message}"),
31            Self::Decode(message) => write!(f, "store value decode failed: {message}"),
32        }
33    }
34}
35
36impl std::error::Error for StoreCodecError {}
37
38pub fn encode_property_value(value: &PropertyValue) -> Result<Vec<u8>> {
39    let mut out = Vec::new();
40    write_property_value(&mut out, value)?;
41    Ok(out)
42}
43
44pub fn decode_property_value(bytes: &[u8]) -> Result<PropertyValue> {
45    let mut reader = Reader::new(bytes);
46    let value = reader.read_property_value()?;
47    reader.finish()?;
48    Ok(value)
49}
50
51pub fn encode_index_request(request: &IndexRequest) -> Result<Vec<u8>> {
52    let mut out = Vec::new();
53    write_index_request(&mut out, request)?;
54    Ok(out)
55}
56
57pub fn decode_index_request(bytes: &[u8]) -> Result<IndexRequest> {
58    let mut reader = Reader::new(bytes);
59    let request = reader.read_index_request()?;
60    reader.finish()?;
61    Ok(request)
62}
63
64pub fn encode_constraint_request(request: &ConstraintRequest) -> Result<Vec<u8>> {
65    let mut out = Vec::new();
66    write_constraint_request(&mut out, request)?;
67    Ok(out)
68}
69
70pub fn decode_constraint_request(bytes: &[u8]) -> Result<ConstraintRequest> {
71    let mut reader = Reader::new(bytes);
72    let request = reader.read_constraint_request()?;
73    reader.finish()?;
74    Ok(request)
75}
76
77pub fn encode_constraint_definitions(defs: &[ConstraintDefinition]) -> Result<Vec<u8>> {
78    let mut out = Vec::new();
79    write_len(&mut out, defs.len())?;
80    for def in defs {
81        write_constraint_definition(&mut out, def)?;
82    }
83    Ok(out)
84}
85
86pub fn decode_constraint_definitions(bytes: &[u8]) -> Result<Vec<ConstraintDefinition>> {
87    let mut reader = Reader::new(bytes);
88    let len = reader.read_len_bounded("constraint definition")?;
89    let mut defs = reader.vec_with_capacity(len, "constraint definition")?;
90    for _ in 0..len {
91        defs.push(reader.read_constraint_definition()?);
92    }
93    reader.finish()?;
94    Ok(defs)
95}
96
97pub fn encode_index_definitions(defs: &[IndexDefinition]) -> Result<Vec<u8>> {
98    let mut out = Vec::new();
99    write_len(&mut out, defs.len())?;
100    for def in defs {
101        write_index_definition(&mut out, def)?;
102    }
103    Ok(out)
104}
105
106pub fn decode_index_definitions(bytes: &[u8]) -> Result<Vec<IndexDefinition>> {
107    let mut reader = Reader::new(bytes);
108    let len = reader.read_len_bounded("index definition")?;
109    let mut defs = reader.vec_with_capacity(len, "index definition")?;
110    for _ in 0..len {
111        defs.push(reader.read_index_definition()?);
112    }
113    reader.finish()?;
114    Ok(defs)
115}
116
117const VALUE_NULL: u8 = 0;
118const VALUE_BOOL: u8 = 1;
119const VALUE_INT: u8 = 2;
120const VALUE_FLOAT: u8 = 3;
121const VALUE_STRING: u8 = 4;
122const VALUE_LIST: u8 = 5;
123const VALUE_MAP: u8 = 6;
124const VALUE_DATE: u8 = 7;
125const VALUE_TIME: u8 = 8;
126const VALUE_LOCAL_TIME: u8 = 9;
127const VALUE_DATE_TIME: u8 = 10;
128const VALUE_LOCAL_DATE_TIME: u8 = 11;
129const VALUE_DURATION: u8 = 12;
130const VALUE_POINT: u8 = 13;
131const VALUE_VECTOR: u8 = 14;
132const VALUE_BINARY: u8 = 15;
133
134const VECTOR_FLOAT64: u8 = 1;
135const VECTOR_FLOAT32: u8 = 2;
136const VECTOR_INTEGER64: u8 = 3;
137const VECTOR_INTEGER32: u8 = 4;
138const VECTOR_INTEGER16: u8 = 5;
139const VECTOR_INTEGER8: u8 = 6;
140
141const INDEX_KIND_RANGE: u8 = 1;
142const INDEX_KIND_TEXT: u8 = 2;
143const INDEX_KIND_POINT: u8 = 3;
144const INDEX_KIND_LOOKUP: u8 = 4;
145const INDEX_KIND_VECTOR: u8 = 5;
146const INDEX_KIND_FULLTEXT: u8 = 6;
147
148const INDEX_ENTITY_NODE: u8 = 1;
149const INDEX_ENTITY_RELATIONSHIP: u8 = 2;
150
151const INDEX_STATE_ONLINE: u8 = 1;
152const INDEX_STATE_POPULATING: u8 = 2;
153
154const CONFIG_NUMBER: u8 = 1;
155const CONFIG_INTEGER: u8 = 2;
156const CONFIG_STRING: u8 = 3;
157const CONFIG_BOOL: u8 = 4;
158const CONFIG_LIST: u8 = 5;
159const CONFIG_MAP: u8 = 6;
160const CONFIG_NULL: u8 = 7;
161
162const CONSTRAINT_KIND_UNIQUE: u8 = 1;
163const CONSTRAINT_KIND_EXISTENCE: u8 = 2;
164const CONSTRAINT_KIND_NODE_KEY: u8 = 3;
165const CONSTRAINT_KIND_RELATIONSHIP_KEY: u8 = 4;
166const CONSTRAINT_KIND_PROPERTY_TYPE: u8 = 5;
167
168const PROPERTY_TYPE_TERM_SCALAR: u8 = 1;
169const PROPERTY_TYPE_TERM_LIST: u8 = 2;
170const PROPERTY_TYPE_TERM_VECTOR: u8 = 3;
171
172const SCALAR_BOOLEAN: u8 = 1;
173const SCALAR_STRING: u8 = 2;
174const SCALAR_INTEGER: u8 = 3;
175const SCALAR_FLOAT: u8 = 4;
176const SCALAR_DATE: u8 = 5;
177const SCALAR_LOCAL_TIME: u8 = 6;
178const SCALAR_ZONED_TIME: u8 = 7;
179const SCALAR_LOCAL_DATETIME: u8 = 8;
180const SCALAR_ZONED_DATETIME: u8 = 9;
181const SCALAR_DURATION: u8 = 10;
182const SCALAR_POINT: u8 = 11;
183const SCALAR_MAP: u8 = 12;
184const SCALAR_ANY: u8 = 13;
185
186const VCOORD_INT8: u8 = 1;
187const VCOORD_INT16: u8 = 2;
188const VCOORD_INT32: u8 = 3;
189const VCOORD_INT64: u8 = 4;
190const VCOORD_FLOAT32: u8 = 5;
191const VCOORD_FLOAT64: u8 = 6;
192
193fn write_index_request(out: &mut Vec<u8>, request: &IndexRequest) -> Result<()> {
194    write_optional_string(out, request.explicit_name.as_deref())?;
195    write_index_kind(out, request.kind);
196    write_index_entity(out, request.entity);
197    write_optional_string(out, request.label.as_deref())?;
198    write_string_vec(out, &request.additional_labels)?;
199    write_string_vec(out, &request.properties)?;
200    write_config_map(out, &request.options)
201}
202
203fn write_index_definition(out: &mut Vec<u8>, def: &IndexDefinition) -> Result<()> {
204    write_string(out, &def.name)?;
205    write_index_kind(out, def.kind);
206    write_index_entity(out, def.entity);
207    write_optional_string(out, def.label.as_deref())?;
208    write_string_vec(out, &def.additional_labels)?;
209    write_string_vec(out, &def.properties)?;
210    write_config_map(out, &def.options)?;
211    write_index_state(out, def.state);
212    Ok(())
213}
214
215fn write_index_kind(out: &mut Vec<u8>, kind: StoredIndexKind) {
216    out.push(match kind {
217        StoredIndexKind::Range => INDEX_KIND_RANGE,
218        StoredIndexKind::Text => INDEX_KIND_TEXT,
219        StoredIndexKind::Point => INDEX_KIND_POINT,
220        StoredIndexKind::Lookup => INDEX_KIND_LOOKUP,
221        StoredIndexKind::Vector => INDEX_KIND_VECTOR,
222        StoredIndexKind::Fulltext => INDEX_KIND_FULLTEXT,
223    });
224}
225
226fn write_index_entity(out: &mut Vec<u8>, entity: StoredIndexEntity) {
227    out.push(match entity {
228        StoredIndexEntity::Node => INDEX_ENTITY_NODE,
229        StoredIndexEntity::Relationship => INDEX_ENTITY_RELATIONSHIP,
230    });
231}
232
233fn write_index_state(out: &mut Vec<u8>, state: StoredIndexState) {
234    out.push(match state {
235        StoredIndexState::Online => INDEX_STATE_ONLINE,
236        StoredIndexState::Populating => INDEX_STATE_POPULATING,
237    });
238}
239
240fn write_constraint_request(out: &mut Vec<u8>, request: &ConstraintRequest) -> Result<()> {
241    write_string(out, &request.name)?;
242    write_constraint_kind(out, &request.kind)?;
243    write_index_entity(out, request.entity);
244    write_string(out, &request.label)?;
245    write_string_vec(out, &request.properties)
246}
247
248fn write_constraint_definition(out: &mut Vec<u8>, def: &ConstraintDefinition) -> Result<()> {
249    write_string(out, &def.name)?;
250    write_constraint_kind(out, &def.kind)?;
251    write_index_entity(out, def.entity);
252    write_string(out, &def.label)?;
253    write_string_vec(out, &def.properties)?;
254    write_optional_string(out, def.owned_index.as_deref())
255}
256
257fn write_constraint_kind(out: &mut Vec<u8>, kind: &StoredConstraintKind) -> Result<()> {
258    match kind {
259        StoredConstraintKind::Unique => out.push(CONSTRAINT_KIND_UNIQUE),
260        StoredConstraintKind::Existence => out.push(CONSTRAINT_KIND_EXISTENCE),
261        StoredConstraintKind::NodeKey => out.push(CONSTRAINT_KIND_NODE_KEY),
262        StoredConstraintKind::RelationshipKey => out.push(CONSTRAINT_KIND_RELATIONSHIP_KEY),
263        StoredConstraintKind::PropertyType(t) => {
264            out.push(CONSTRAINT_KIND_PROPERTY_TYPE);
265            write_property_type(out, t)?;
266        }
267    }
268    Ok(())
269}
270
271fn write_property_type(out: &mut Vec<u8>, t: &StoredPropertyType) -> Result<()> {
272    write_len(out, t.alternatives.len())?;
273    for term in &t.alternatives {
274        write_property_type_term(out, term)?;
275    }
276    Ok(())
277}
278
279fn write_property_type_term(out: &mut Vec<u8>, term: &StoredPropertyTypeTerm) -> Result<()> {
280    match term {
281        StoredPropertyTypeTerm::Scalar(s) => {
282            out.push(PROPERTY_TYPE_TERM_SCALAR);
283            out.push(scalar_tag(*s));
284        }
285        StoredPropertyTypeTerm::List { inner, not_null } => {
286            out.push(PROPERTY_TYPE_TERM_LIST);
287            out.push(u8::from(*not_null));
288            write_property_type_term(out, inner)?;
289        }
290        StoredPropertyTypeTerm::Vector { coord, dimension } => {
291            out.push(PROPERTY_TYPE_TERM_VECTOR);
292            out.push(vector_coord_tag(*coord));
293            write_u32(out, *dimension);
294        }
295    }
296    Ok(())
297}
298
299fn scalar_tag(s: StoredScalarType) -> u8 {
300    match s {
301        StoredScalarType::Boolean => SCALAR_BOOLEAN,
302        StoredScalarType::String => SCALAR_STRING,
303        StoredScalarType::Integer => SCALAR_INTEGER,
304        StoredScalarType::Float => SCALAR_FLOAT,
305        StoredScalarType::Date => SCALAR_DATE,
306        StoredScalarType::LocalTime => SCALAR_LOCAL_TIME,
307        StoredScalarType::ZonedTime => SCALAR_ZONED_TIME,
308        StoredScalarType::LocalDateTime => SCALAR_LOCAL_DATETIME,
309        StoredScalarType::ZonedDateTime => SCALAR_ZONED_DATETIME,
310        StoredScalarType::Duration => SCALAR_DURATION,
311        StoredScalarType::Point => SCALAR_POINT,
312        StoredScalarType::Map => SCALAR_MAP,
313        StoredScalarType::Any => SCALAR_ANY,
314    }
315}
316
317fn vector_coord_tag(c: StoredVectorCoordType) -> u8 {
318    match c {
319        StoredVectorCoordType::Int8 => VCOORD_INT8,
320        StoredVectorCoordType::Int16 => VCOORD_INT16,
321        StoredVectorCoordType::Int32 => VCOORD_INT32,
322        StoredVectorCoordType::Int64 => VCOORD_INT64,
323        StoredVectorCoordType::Float32 => VCOORD_FLOAT32,
324        StoredVectorCoordType::Float64 => VCOORD_FLOAT64,
325    }
326}
327
328fn write_config_map(out: &mut Vec<u8>, values: &BTreeMap<String, IndexConfigValue>) -> Result<()> {
329    write_len(out, values.len())?;
330    for (key, value) in values {
331        write_string(out, key)?;
332        write_config_value(out, value)?;
333    }
334    Ok(())
335}
336
337fn write_config_value(out: &mut Vec<u8>, value: &IndexConfigValue) -> Result<()> {
338    match value {
339        IndexConfigValue::Number(value) => {
340            out.push(CONFIG_NUMBER);
341            write_f64(out, *value);
342        }
343        IndexConfigValue::Integer(value) => {
344            out.push(CONFIG_INTEGER);
345            write_i64(out, *value);
346        }
347        IndexConfigValue::String(value) => {
348            out.push(CONFIG_STRING);
349            write_string(out, value)?;
350        }
351        IndexConfigValue::Bool(value) => {
352            out.push(CONFIG_BOOL);
353            out.push(u8::from(*value));
354        }
355        IndexConfigValue::List(values) => {
356            out.push(CONFIG_LIST);
357            write_len(out, values.len())?;
358            for value in values {
359                write_config_value(out, value)?;
360            }
361        }
362        IndexConfigValue::Map(values) => {
363            out.push(CONFIG_MAP);
364            write_config_map(out, values)?;
365        }
366        IndexConfigValue::Null => out.push(CONFIG_NULL),
367    }
368    Ok(())
369}
370
371fn write_property_value(out: &mut Vec<u8>, value: &PropertyValue) -> Result<()> {
372    match value {
373        PropertyValue::Null => out.push(VALUE_NULL),
374        PropertyValue::Bool(value) => {
375            out.push(VALUE_BOOL);
376            out.push(u8::from(*value));
377        }
378        PropertyValue::Int(value) => {
379            out.push(VALUE_INT);
380            write_i64(out, *value);
381        }
382        PropertyValue::Float(value) => {
383            out.push(VALUE_FLOAT);
384            write_f64(out, *value);
385        }
386        PropertyValue::String(value) => {
387            out.push(VALUE_STRING);
388            write_string(out, value)?;
389        }
390        PropertyValue::List(values) => {
391            out.push(VALUE_LIST);
392            write_len(out, values.len())?;
393            for value in values {
394                write_property_value(out, value)?;
395            }
396        }
397        PropertyValue::Map(values) => {
398            out.push(VALUE_MAP);
399            write_len(out, values.len())?;
400            for (key, value) in values {
401                write_string(out, key)?;
402                write_property_value(out, value)?;
403            }
404        }
405        PropertyValue::Date(value) => {
406            out.push(VALUE_DATE);
407            write_i32(out, value.year);
408            write_u32(out, value.month);
409            write_u32(out, value.day);
410        }
411        PropertyValue::Time(value) => {
412            out.push(VALUE_TIME);
413            write_time_fields(
414                out,
415                value.hour,
416                value.minute,
417                value.second,
418                value.nanosecond,
419            );
420            write_i32(out, value.offset_seconds);
421        }
422        PropertyValue::LocalTime(value) => {
423            out.push(VALUE_LOCAL_TIME);
424            write_time_fields(
425                out,
426                value.hour,
427                value.minute,
428                value.second,
429                value.nanosecond,
430            );
431        }
432        PropertyValue::DateTime(value) => {
433            out.push(VALUE_DATE_TIME);
434            write_date_fields(out, value.year, value.month, value.day);
435            write_time_fields(
436                out,
437                value.hour,
438                value.minute,
439                value.second,
440                value.nanosecond,
441            );
442            write_i32(out, value.offset_seconds);
443        }
444        PropertyValue::LocalDateTime(value) => {
445            out.push(VALUE_LOCAL_DATE_TIME);
446            write_date_fields(out, value.year, value.month, value.day);
447            write_time_fields(
448                out,
449                value.hour,
450                value.minute,
451                value.second,
452                value.nanosecond,
453            );
454        }
455        PropertyValue::Duration(value) => {
456            out.push(VALUE_DURATION);
457            write_i64(out, value.months);
458            write_i64(out, value.days);
459            write_i64(out, value.seconds);
460            write_i64(out, value.nanoseconds);
461        }
462        PropertyValue::Point(value) => {
463            out.push(VALUE_POINT);
464            write_f64(out, value.x);
465            write_f64(out, value.y);
466            match value.z {
467                Some(z) => {
468                    out.push(1);
469                    write_f64(out, z);
470                }
471                None => out.push(0),
472            }
473            write_u32(out, value.srid);
474        }
475        PropertyValue::Vector(value) => {
476            out.push(VALUE_VECTOR);
477            write_vector(out, value)?;
478        }
479        PropertyValue::Binary(value) => {
480            out.push(VALUE_BINARY);
481            write_len(out, value.segments().len())?;
482            for segment in value.segments() {
483                write_bytes(out, segment)?;
484            }
485        }
486    }
487    Ok(())
488}
489
490fn write_date_fields(out: &mut Vec<u8>, year: i32, month: u32, day: u32) {
491    write_i32(out, year);
492    write_u32(out, month);
493    write_u32(out, day);
494}
495
496fn write_time_fields(out: &mut Vec<u8>, hour: u32, minute: u32, second: u32, nanosecond: u32) {
497    write_u32(out, hour);
498    write_u32(out, minute);
499    write_u32(out, second);
500    write_u32(out, nanosecond);
501}
502
503fn write_vector(out: &mut Vec<u8>, vector: &LoraVector) -> Result<()> {
504    write_len(out, vector.dimension)?;
505    match &vector.values {
506        VectorValues::Float64(values) => {
507            out.push(VECTOR_FLOAT64);
508            write_len(out, values.len())?;
509            for value in values {
510                write_f64(out, *value);
511            }
512        }
513        VectorValues::Float32(values) => {
514            out.push(VECTOR_FLOAT32);
515            write_len(out, values.len())?;
516            for value in values {
517                write_f32(out, *value);
518            }
519        }
520        VectorValues::Integer64(values) => {
521            out.push(VECTOR_INTEGER64);
522            write_len(out, values.len())?;
523            for value in values {
524                write_i64(out, *value);
525            }
526        }
527        VectorValues::Integer32(values) => {
528            out.push(VECTOR_INTEGER32);
529            write_len(out, values.len())?;
530            for value in values {
531                write_i32(out, *value);
532            }
533        }
534        VectorValues::Integer16(values) => {
535            out.push(VECTOR_INTEGER16);
536            write_len(out, values.len())?;
537            for value in values {
538                write_i16(out, *value);
539            }
540        }
541        VectorValues::Integer8(values) => {
542            out.push(VECTOR_INTEGER8);
543            write_len(out, values.len())?;
544            for value in values {
545                out.push(*value as u8);
546            }
547        }
548    }
549    Ok(())
550}
551
552fn write_optional_string(out: &mut Vec<u8>, value: Option<&str>) -> Result<()> {
553    match value {
554        Some(value) => {
555            out.push(1);
556            write_string(out, value)?;
557        }
558        None => out.push(0),
559    }
560    Ok(())
561}
562
563fn write_len(out: &mut Vec<u8>, len: usize) -> Result<()> {
564    write_u64(
565        out,
566        u64::try_from(len)
567            .map_err(|_| StoreCodecError::Encode("length does not fit in u64".into()))?,
568    );
569    Ok(())
570}
571
572fn write_bytes(out: &mut Vec<u8>, bytes: &[u8]) -> Result<()> {
573    write_len(out, bytes.len())?;
574    out.extend_from_slice(bytes);
575    Ok(())
576}
577
578fn write_string(out: &mut Vec<u8>, value: &str) -> Result<()> {
579    write_bytes(out, value.as_bytes())
580}
581
582fn write_string_vec(out: &mut Vec<u8>, values: &[String]) -> Result<()> {
583    write_len(out, values.len())?;
584    for value in values {
585        write_string(out, value)?;
586    }
587    Ok(())
588}
589
590fn write_i16(out: &mut Vec<u8>, value: i16) {
591    out.extend_from_slice(&value.to_le_bytes());
592}
593
594fn write_i32(out: &mut Vec<u8>, value: i32) {
595    out.extend_from_slice(&value.to_le_bytes());
596}
597
598fn write_u32(out: &mut Vec<u8>, value: u32) {
599    out.extend_from_slice(&value.to_le_bytes());
600}
601
602fn write_i64(out: &mut Vec<u8>, value: i64) {
603    out.extend_from_slice(&value.to_le_bytes());
604}
605
606fn write_u64(out: &mut Vec<u8>, value: u64) {
607    out.extend_from_slice(&value.to_le_bytes());
608}
609
610fn write_f32(out: &mut Vec<u8>, value: f32) {
611    out.extend_from_slice(&value.to_bits().to_le_bytes());
612}
613
614fn write_f64(out: &mut Vec<u8>, value: f64) {
615    out.extend_from_slice(&value.to_bits().to_le_bytes());
616}
617
618struct Reader<'a> {
619    bytes: &'a [u8],
620    offset: usize,
621}
622
623impl<'a> Reader<'a> {
624    fn new(bytes: &'a [u8]) -> Self {
625        Self { bytes, offset: 0 }
626    }
627
628    fn finish(&self) -> Result<()> {
629        if self.offset == self.bytes.len() {
630            Ok(())
631        } else {
632            Err(StoreCodecError::Decode(format!(
633                "trailing bytes: {}",
634                self.bytes.len() - self.offset
635            )))
636        }
637    }
638
639    fn read_exact(&mut self, len: usize) -> Result<&'a [u8]> {
640        let end = self
641            .offset
642            .checked_add(len)
643            .ok_or_else(|| StoreCodecError::Decode("offset overflow".into()))?;
644        if end > self.bytes.len() {
645            return Err(StoreCodecError::Decode("truncated input".into()));
646        }
647        let out = &self.bytes[self.offset..end];
648        self.offset = end;
649        Ok(out)
650    }
651
652    fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
653        self.read_exact(N)?
654            .try_into()
655            .map_err(|_| StoreCodecError::Decode("fixed-width field truncated".into()))
656    }
657
658    fn read_u8(&mut self) -> Result<u8> {
659        Ok(self.read_exact(1)?[0])
660    }
661
662    fn read_i8(&mut self) -> Result<i8> {
663        Ok(self.read_u8()? as i8)
664    }
665
666    fn read_i16(&mut self) -> Result<i16> {
667        Ok(i16::from_le_bytes(self.read_array()?))
668    }
669
670    fn read_i32(&mut self) -> Result<i32> {
671        Ok(i32::from_le_bytes(self.read_array()?))
672    }
673
674    fn read_u32(&mut self) -> Result<u32> {
675        Ok(u32::from_le_bytes(self.read_array()?))
676    }
677
678    fn read_i64(&mut self) -> Result<i64> {
679        Ok(i64::from_le_bytes(self.read_array()?))
680    }
681
682    fn read_u64(&mut self) -> Result<u64> {
683        Ok(u64::from_le_bytes(self.read_array()?))
684    }
685
686    fn read_f32(&mut self) -> Result<f32> {
687        Ok(f32::from_bits(self.read_u32()?))
688    }
689
690    fn read_f64(&mut self) -> Result<f64> {
691        Ok(f64::from_bits(self.read_u64()?))
692    }
693
694    fn read_len(&mut self) -> Result<usize> {
695        usize::try_from(self.read_u64()?)
696            .map_err(|_| StoreCodecError::Decode("length overflows usize".into()))
697    }
698
699    fn remaining(&self) -> usize {
700        self.bytes.len().saturating_sub(self.offset)
701    }
702
703    fn read_len_bounded(&mut self, label: &str) -> Result<usize> {
704        let len = self.read_len()?;
705        if len > self.remaining() {
706            return Err(StoreCodecError::Decode(format!(
707                "{label} count {len} exceeds remaining input"
708            )));
709        }
710        Ok(len)
711    }
712
713    fn vec_with_capacity<T>(&self, len: usize, label: &str) -> Result<Vec<T>> {
714        let mut values = Vec::new();
715        values.try_reserve(len).map_err(|_| {
716            StoreCodecError::Decode(format!("{label} count {len} is too large to allocate"))
717        })?;
718        Ok(values)
719    }
720
721    fn read_bytes(&mut self) -> Result<&'a [u8]> {
722        let len = self.read_len()?;
723        self.read_exact(len)
724    }
725
726    fn read_string(&mut self) -> Result<String> {
727        let bytes = self.read_bytes()?;
728        std::str::from_utf8(bytes)
729            .map(|value| value.to_string())
730            .map_err(|e| StoreCodecError::Decode(format!("invalid UTF-8 string: {e}")))
731    }
732
733    fn read_string_vec(&mut self) -> Result<Vec<String>> {
734        let len = self.read_len_bounded("string")?;
735        let mut values = self.vec_with_capacity(len, "string")?;
736        for _ in 0..len {
737            values.push(self.read_string()?);
738        }
739        Ok(values)
740    }
741
742    fn read_optional_string(&mut self) -> Result<Option<String>> {
743        match self.read_u8()? {
744            0 => Ok(None),
745            1 => Ok(Some(self.read_string()?)),
746            tag => Err(StoreCodecError::Decode(format!(
747                "invalid optional string tag {tag}"
748            ))),
749        }
750    }
751
752    fn read_index_request(&mut self) -> Result<IndexRequest> {
753        Ok(IndexRequest {
754            explicit_name: self.read_optional_string()?,
755            kind: self.read_index_kind()?,
756            entity: self.read_index_entity()?,
757            label: self.read_optional_string()?,
758            additional_labels: self.read_string_vec()?,
759            properties: self.read_string_vec()?,
760            options: self.read_config_map()?,
761        })
762    }
763
764    fn read_index_definition(&mut self) -> Result<IndexDefinition> {
765        Ok(IndexDefinition {
766            name: self.read_string()?,
767            kind: self.read_index_kind()?,
768            entity: self.read_index_entity()?,
769            label: self.read_optional_string()?,
770            additional_labels: self.read_string_vec()?,
771            properties: self.read_string_vec()?,
772            options: self.read_config_map()?,
773            state: self.read_index_state()?,
774        })
775    }
776
777    fn read_index_kind(&mut self) -> Result<StoredIndexKind> {
778        match self.read_u8()? {
779            INDEX_KIND_RANGE => Ok(StoredIndexKind::Range),
780            INDEX_KIND_TEXT => Ok(StoredIndexKind::Text),
781            INDEX_KIND_POINT => Ok(StoredIndexKind::Point),
782            INDEX_KIND_LOOKUP => Ok(StoredIndexKind::Lookup),
783            INDEX_KIND_VECTOR => Ok(StoredIndexKind::Vector),
784            INDEX_KIND_FULLTEXT => Ok(StoredIndexKind::Fulltext),
785            tag => Err(StoreCodecError::Decode(format!(
786                "invalid index kind tag {tag}"
787            ))),
788        }
789    }
790
791    fn read_index_entity(&mut self) -> Result<StoredIndexEntity> {
792        match self.read_u8()? {
793            INDEX_ENTITY_NODE => Ok(StoredIndexEntity::Node),
794            INDEX_ENTITY_RELATIONSHIP => Ok(StoredIndexEntity::Relationship),
795            tag => Err(StoreCodecError::Decode(format!(
796                "invalid index entity tag {tag}"
797            ))),
798        }
799    }
800
801    fn read_index_state(&mut self) -> Result<StoredIndexState> {
802        match self.read_u8()? {
803            INDEX_STATE_ONLINE => Ok(StoredIndexState::Online),
804            INDEX_STATE_POPULATING => Ok(StoredIndexState::Populating),
805            tag => Err(StoreCodecError::Decode(format!(
806                "invalid index state tag {tag}"
807            ))),
808        }
809    }
810
811    fn read_constraint_request(&mut self) -> Result<ConstraintRequest> {
812        Ok(ConstraintRequest {
813            name: self.read_string()?,
814            kind: self.read_constraint_kind()?,
815            entity: self.read_index_entity()?,
816            label: self.read_string()?,
817            properties: self.read_string_vec()?,
818        })
819    }
820
821    fn read_constraint_definition(&mut self) -> Result<ConstraintDefinition> {
822        Ok(ConstraintDefinition {
823            name: self.read_string()?,
824            kind: self.read_constraint_kind()?,
825            entity: self.read_index_entity()?,
826            label: self.read_string()?,
827            properties: self.read_string_vec()?,
828            owned_index: self.read_optional_string()?,
829        })
830    }
831
832    fn read_constraint_kind(&mut self) -> Result<StoredConstraintKind> {
833        match self.read_u8()? {
834            CONSTRAINT_KIND_UNIQUE => Ok(StoredConstraintKind::Unique),
835            CONSTRAINT_KIND_EXISTENCE => Ok(StoredConstraintKind::Existence),
836            CONSTRAINT_KIND_NODE_KEY => Ok(StoredConstraintKind::NodeKey),
837            CONSTRAINT_KIND_RELATIONSHIP_KEY => Ok(StoredConstraintKind::RelationshipKey),
838            CONSTRAINT_KIND_PROPERTY_TYPE => Ok(StoredConstraintKind::PropertyType(
839                self.read_property_type()?,
840            )),
841            tag => Err(StoreCodecError::Decode(format!(
842                "invalid constraint kind tag {tag}"
843            ))),
844        }
845    }
846
847    fn read_property_type(&mut self) -> Result<StoredPropertyType> {
848        let len = self.read_len_bounded("property type alternative")?;
849        let mut alternatives = self.vec_with_capacity(len, "property type alternative")?;
850        for _ in 0..len {
851            alternatives.push(self.read_property_type_term()?);
852        }
853        Ok(StoredPropertyType { alternatives })
854    }
855
856    fn read_property_type_term(&mut self) -> Result<StoredPropertyTypeTerm> {
857        match self.read_u8()? {
858            PROPERTY_TYPE_TERM_SCALAR => {
859                Ok(StoredPropertyTypeTerm::Scalar(self.read_scalar_type()?))
860            }
861            PROPERTY_TYPE_TERM_LIST => {
862                let not_null = self.read_u8()? != 0;
863                let inner = Box::new(self.read_property_type_term()?);
864                Ok(StoredPropertyTypeTerm::List { inner, not_null })
865            }
866            PROPERTY_TYPE_TERM_VECTOR => {
867                let coord = self.read_vector_coord_type()?;
868                let dimension = self.read_u32()?;
869                Ok(StoredPropertyTypeTerm::Vector { coord, dimension })
870            }
871            tag => Err(StoreCodecError::Decode(format!(
872                "invalid property-type term tag {tag}"
873            ))),
874        }
875    }
876
877    fn read_scalar_type(&mut self) -> Result<StoredScalarType> {
878        match self.read_u8()? {
879            SCALAR_BOOLEAN => Ok(StoredScalarType::Boolean),
880            SCALAR_STRING => Ok(StoredScalarType::String),
881            SCALAR_INTEGER => Ok(StoredScalarType::Integer),
882            SCALAR_FLOAT => Ok(StoredScalarType::Float),
883            SCALAR_DATE => Ok(StoredScalarType::Date),
884            SCALAR_LOCAL_TIME => Ok(StoredScalarType::LocalTime),
885            SCALAR_ZONED_TIME => Ok(StoredScalarType::ZonedTime),
886            SCALAR_LOCAL_DATETIME => Ok(StoredScalarType::LocalDateTime),
887            SCALAR_ZONED_DATETIME => Ok(StoredScalarType::ZonedDateTime),
888            SCALAR_DURATION => Ok(StoredScalarType::Duration),
889            SCALAR_POINT => Ok(StoredScalarType::Point),
890            SCALAR_MAP => Ok(StoredScalarType::Map),
891            SCALAR_ANY => Ok(StoredScalarType::Any),
892            tag => Err(StoreCodecError::Decode(format!(
893                "invalid scalar type tag {tag}"
894            ))),
895        }
896    }
897
898    fn read_vector_coord_type(&mut self) -> Result<StoredVectorCoordType> {
899        match self.read_u8()? {
900            VCOORD_INT8 => Ok(StoredVectorCoordType::Int8),
901            VCOORD_INT16 => Ok(StoredVectorCoordType::Int16),
902            VCOORD_INT32 => Ok(StoredVectorCoordType::Int32),
903            VCOORD_INT64 => Ok(StoredVectorCoordType::Int64),
904            VCOORD_FLOAT32 => Ok(StoredVectorCoordType::Float32),
905            VCOORD_FLOAT64 => Ok(StoredVectorCoordType::Float64),
906            tag => Err(StoreCodecError::Decode(format!(
907                "invalid vector coord type tag {tag}"
908            ))),
909        }
910    }
911
912    fn read_config_map(&mut self) -> Result<BTreeMap<String, IndexConfigValue>> {
913        let len = self.read_len_bounded("index config map entry")?;
914        let mut values = BTreeMap::new();
915        for _ in 0..len {
916            values.insert(self.read_string()?, self.read_config_value()?);
917        }
918        Ok(values)
919    }
920
921    fn read_config_value(&mut self) -> Result<IndexConfigValue> {
922        Ok(match self.read_u8()? {
923            CONFIG_NUMBER => IndexConfigValue::Number(self.read_f64()?),
924            CONFIG_INTEGER => IndexConfigValue::Integer(self.read_i64()?),
925            CONFIG_STRING => IndexConfigValue::String(self.read_string()?),
926            CONFIG_BOOL => IndexConfigValue::Bool(self.read_u8()? != 0),
927            CONFIG_LIST => {
928                let len = self.read_len_bounded("index config list value")?;
929                let mut values = self.vec_with_capacity(len, "index config list value")?;
930                for _ in 0..len {
931                    values.push(self.read_config_value()?);
932                }
933                IndexConfigValue::List(values)
934            }
935            CONFIG_MAP => IndexConfigValue::Map(self.read_config_map()?),
936            CONFIG_NULL => IndexConfigValue::Null,
937            tag => {
938                return Err(StoreCodecError::Decode(format!(
939                    "invalid index config value tag {tag}"
940                )));
941            }
942        })
943    }
944
945    fn read_property_value(&mut self) -> Result<PropertyValue> {
946        Ok(match self.read_u8()? {
947            VALUE_NULL => PropertyValue::Null,
948            VALUE_BOOL => PropertyValue::Bool(self.read_u8()? != 0),
949            VALUE_INT => PropertyValue::Int(self.read_i64()?),
950            VALUE_FLOAT => PropertyValue::Float(self.read_f64()?),
951            VALUE_STRING => PropertyValue::String(self.read_string()?),
952            VALUE_LIST => {
953                let len = self.read_len_bounded("property list value")?;
954                let mut values = self.vec_with_capacity(len, "property list value")?;
955                for _ in 0..len {
956                    values.push(self.read_property_value()?);
957                }
958                PropertyValue::List(values)
959            }
960            VALUE_MAP => {
961                let len = self.read_len_bounded("property map entry")?;
962                let mut values = BTreeMap::new();
963                for _ in 0..len {
964                    values.insert(self.read_string()?, self.read_property_value()?);
965                }
966                PropertyValue::Map(values)
967            }
968            VALUE_DATE => PropertyValue::Date(LoraDate {
969                year: self.read_i32()?,
970                month: self.read_u32()?,
971                day: self.read_u32()?,
972            }),
973            VALUE_TIME => PropertyValue::Time(LoraTime {
974                hour: self.read_u32()?,
975                minute: self.read_u32()?,
976                second: self.read_u32()?,
977                nanosecond: self.read_u32()?,
978                offset_seconds: self.read_i32()?,
979            }),
980            VALUE_LOCAL_TIME => PropertyValue::LocalTime(LoraLocalTime {
981                hour: self.read_u32()?,
982                minute: self.read_u32()?,
983                second: self.read_u32()?,
984                nanosecond: self.read_u32()?,
985            }),
986            VALUE_DATE_TIME => PropertyValue::DateTime(LoraDateTime {
987                year: self.read_i32()?,
988                month: self.read_u32()?,
989                day: self.read_u32()?,
990                hour: self.read_u32()?,
991                minute: self.read_u32()?,
992                second: self.read_u32()?,
993                nanosecond: self.read_u32()?,
994                offset_seconds: self.read_i32()?,
995            }),
996            VALUE_LOCAL_DATE_TIME => PropertyValue::LocalDateTime(LoraLocalDateTime {
997                year: self.read_i32()?,
998                month: self.read_u32()?,
999                day: self.read_u32()?,
1000                hour: self.read_u32()?,
1001                minute: self.read_u32()?,
1002                second: self.read_u32()?,
1003                nanosecond: self.read_u32()?,
1004            }),
1005            VALUE_DURATION => PropertyValue::Duration(LoraDuration {
1006                months: self.read_i64()?,
1007                days: self.read_i64()?,
1008                seconds: self.read_i64()?,
1009                nanoseconds: self.read_i64()?,
1010            }),
1011            VALUE_POINT => {
1012                let x = self.read_f64()?;
1013                let y = self.read_f64()?;
1014                let z = match self.read_u8()? {
1015                    0 => None,
1016                    1 => Some(self.read_f64()?),
1017                    tag => {
1018                        return Err(StoreCodecError::Decode(format!(
1019                            "invalid point z-presence tag {tag}"
1020                        )));
1021                    }
1022                };
1023                PropertyValue::Point(LoraPoint {
1024                    x,
1025                    y,
1026                    z,
1027                    srid: self.read_u32()?,
1028                })
1029            }
1030            VALUE_VECTOR => PropertyValue::Vector(self.read_vector()?),
1031            VALUE_BINARY => PropertyValue::Binary(self.read_binary()?),
1032            tag => {
1033                return Err(StoreCodecError::Decode(format!(
1034                    "invalid property value tag {tag}"
1035                )));
1036            }
1037        })
1038    }
1039
1040    fn read_vector(&mut self) -> Result<LoraVector> {
1041        let dimension = self.read_len()?;
1042        let values = match self.read_u8()? {
1043            VECTOR_FLOAT64 => {
1044                read_vec(self, |reader| reader.read_f64()).map(VectorValues::Float64)?
1045            }
1046            VECTOR_FLOAT32 => {
1047                read_vec(self, |reader| reader.read_f32()).map(VectorValues::Float32)?
1048            }
1049            VECTOR_INTEGER64 => {
1050                read_vec(self, |reader| reader.read_i64()).map(VectorValues::Integer64)?
1051            }
1052            VECTOR_INTEGER32 => {
1053                read_vec(self, |reader| reader.read_i32()).map(VectorValues::Integer32)?
1054            }
1055            VECTOR_INTEGER16 => {
1056                read_vec(self, |reader| reader.read_i16()).map(VectorValues::Integer16)?
1057            }
1058            VECTOR_INTEGER8 => {
1059                read_vec(self, |reader| reader.read_i8()).map(VectorValues::Integer8)?
1060            }
1061            tag => {
1062                return Err(StoreCodecError::Decode(format!(
1063                    "unknown vector value tag {tag}"
1064                )))
1065            }
1066        };
1067        if values.len() != dimension {
1068            return Err(StoreCodecError::Decode(format!(
1069                "vector dimension mismatch: declared {dimension}, got {}",
1070                values.len()
1071            )));
1072        }
1073        Ok(LoraVector { dimension, values })
1074    }
1075
1076    fn read_binary(&mut self) -> Result<LoraBinary> {
1077        let len = self.read_len_bounded("binary segment")?;
1078        let mut segments = self.vec_with_capacity(len, "binary segment")?;
1079        for _ in 0..len {
1080            segments.push(self.read_bytes()?.to_vec());
1081        }
1082        Ok(LoraBinary::from_segments(segments))
1083    }
1084}
1085
1086fn read_vec<T>(
1087    reader: &mut Reader<'_>,
1088    mut read_one: impl FnMut(&mut Reader<'_>) -> Result<T>,
1089) -> Result<Vec<T>> {
1090    let len = reader.read_len()?;
1091    if len > reader.remaining() {
1092        return Err(StoreCodecError::Decode(format!(
1093            "vector value count {len} exceeds remaining input"
1094        )));
1095    }
1096    let mut values = reader.vec_with_capacity(len, "vector value")?;
1097    for _ in 0..len {
1098        values.push(read_one(reader)?);
1099    }
1100    Ok(values)
1101}
1102
1103#[cfg(test)]
1104mod tests {
1105    use super::*;
1106    use crate::{IndexConfigValue, StoredIndexEntity, StoredIndexKind};
1107
1108    #[test]
1109    fn property_value_roundtrips_nested_values() {
1110        let value = PropertyValue::Map(BTreeMap::from([
1111            ("name".into(), PropertyValue::String("Ada".into())),
1112            (
1113                "scores".into(),
1114                PropertyValue::List(vec![PropertyValue::Int(1), PropertyValue::Float(-2.5)]),
1115            ),
1116        ]));
1117
1118        let bytes = encode_property_value(&value).unwrap();
1119        assert_eq!(decode_property_value(&bytes).unwrap(), value);
1120    }
1121
1122    #[test]
1123    fn property_value_rejects_oversized_list_length() {
1124        let mut bytes = vec![VALUE_LIST];
1125        bytes.extend_from_slice(&u64::MAX.to_le_bytes());
1126
1127        let err = decode_property_value(&bytes).unwrap_err();
1128        assert!(matches!(err, StoreCodecError::Decode(_)));
1129        assert!(err.to_string().contains("exceeds remaining input"));
1130    }
1131
1132    #[test]
1133    fn index_request_roundtrips_options() {
1134        let request = IndexRequest {
1135            explicit_name: Some("idx_person_name".into()),
1136            kind: StoredIndexKind::Text,
1137            entity: StoredIndexEntity::Node,
1138            label: Some("Person".into()),
1139            additional_labels: Vec::new(),
1140            properties: vec!["name".into()],
1141            options: BTreeMap::from([(
1142                "indexConfig".into(),
1143                IndexConfigValue::Map(BTreeMap::from([(
1144                    "trigram.min".into(),
1145                    IndexConfigValue::Integer(3),
1146                )])),
1147            )]),
1148        };
1149
1150        let bytes = encode_index_request(&request).unwrap();
1151        assert_eq!(decode_index_request(&bytes).unwrap(), request);
1152    }
1153
1154    #[test]
1155    fn index_definition_vec_roundtrips_state() {
1156        let defs = vec![IndexDefinition {
1157            name: "idx_person_age".into(),
1158            kind: StoredIndexKind::Range,
1159            entity: StoredIndexEntity::Node,
1160            label: Some("Person".into()),
1161            additional_labels: Vec::new(),
1162            properties: vec!["age".into()],
1163            options: BTreeMap::new(),
1164            state: StoredIndexState::Online,
1165        }];
1166
1167        let bytes = encode_index_definitions(&defs).unwrap();
1168        assert_eq!(decode_index_definitions(&bytes).unwrap(), defs);
1169    }
1170}