1use 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}