1use std::collections::BTreeMap;
9use std::fmt;
10
11use crate::{
12 ConstraintBacking, ConstraintDefinition, ConstraintRequest, IndexConfigValue, IndexDefinition,
13 IndexRequest, LoraBinary, LoraDate, LoraDateTime, LoraDuration, LoraLocalDateTime,
14 LoraLocalTime, LoraPoint, LoraTime, LoraVector, PropertyValue, StoredConstraintKind,
15 StoredIndexEntity, StoredIndexKind, StoredIndexState, StoredPropertyType,
16 StoredPropertyTypeTerm, StoredScalarType, StoredVectorCoordType, VectorValues, ZoneId,
17};
18
19type Result<T> = std::result::Result<T, StoreCodecError>;
20
21pub const MAX_VALUE_NESTING: usize = lora_ast::limits::MAX_VALUE_NESTING;
26
27fn nesting_error(kind: fn(String) -> StoreCodecError) -> StoreCodecError {
28 kind(format!(
29 "value nests lists and maps more than {MAX_VALUE_NESTING} deep"
30 ))
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum StoreCodecError {
35 Encode(String),
36 Decode(String),
37}
38
39impl fmt::Display for StoreCodecError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 Self::Encode(message) => write!(f, "store value encode failed: {message}"),
43 Self::Decode(message) => write!(f, "store value decode failed: {message}"),
44 }
45 }
46}
47
48impl std::error::Error for StoreCodecError {}
49
50pub fn encode_property_value(value: &PropertyValue) -> Result<Vec<u8>> {
51 let mut out = Vec::new();
52 write_property_value(&mut out, value, 0, MAX_VALUE_NESTING)?;
53 Ok(out)
54}
55
56pub fn decode_property_value(bytes: &[u8]) -> Result<PropertyValue> {
57 let mut reader = Reader::new(bytes);
58 let value = reader.read_property_value()?;
59 reader.finish()?;
60 Ok(value)
61}
62
63pub(crate) fn encode_stored_property_value(value: &PropertyValue) -> Result<Vec<u8>> {
68 let mut out = Vec::new();
69 write_property_value(&mut out, value, 0, usize::MAX)?;
70 Ok(out)
71}
72
73pub(crate) fn decode_stored_property_value(bytes: &[u8]) -> Result<PropertyValue> {
76 let mut reader = Reader::new(bytes);
77 reader.max_depth = usize::MAX;
78 let value = reader.read_property_value()?;
79 reader.finish()?;
80 Ok(value)
81}
82
83pub fn encode_index_request(request: &IndexRequest) -> Result<Vec<u8>> {
84 let mut out = Vec::new();
85 write_index_request(&mut out, request)?;
86 Ok(out)
87}
88
89pub fn decode_index_request(bytes: &[u8]) -> Result<IndexRequest> {
90 let mut reader = Reader::new(bytes);
91 let request = reader.read_index_request()?;
92 reader.finish()?;
93 Ok(request)
94}
95
96pub fn encode_constraint_request(request: &ConstraintRequest) -> Result<Vec<u8>> {
97 let mut out = Vec::new();
98 write_constraint_request(&mut out, request)?;
99 Ok(out)
100}
101
102pub fn decode_constraint_request(bytes: &[u8]) -> Result<ConstraintRequest> {
103 let mut reader = Reader::new(bytes);
104 let request = reader.read_constraint_request()?;
105 reader.finish()?;
106 Ok(request)
107}
108
109pub fn encode_constraint_definitions(defs: &[ConstraintDefinition]) -> Result<Vec<u8>> {
110 let mut out = Vec::new();
111 write_len(&mut out, defs.len())?;
112 for def in defs {
113 write_constraint_definition(&mut out, def)?;
114 }
115 Ok(out)
116}
117
118pub fn decode_constraint_definitions(bytes: &[u8]) -> Result<Vec<ConstraintDefinition>> {
119 let mut reader = Reader::new(bytes);
120 let len = reader.read_len_bounded("constraint definition")?;
121 let mut defs = reader.vec_with_capacity(len, "constraint definition")?;
122 for _ in 0..len {
123 defs.push(reader.read_constraint_definition()?);
124 }
125 reader.finish()?;
126 Ok(defs)
127}
128
129pub fn encode_index_definitions(defs: &[IndexDefinition]) -> Result<Vec<u8>> {
130 let mut out = Vec::new();
131 write_len(&mut out, defs.len())?;
132 for def in defs {
133 write_index_definition(&mut out, def)?;
134 }
135 Ok(out)
136}
137
138pub fn decode_index_definitions(bytes: &[u8]) -> Result<Vec<IndexDefinition>> {
139 let mut reader = Reader::new(bytes);
140 let len = reader.read_len_bounded("index definition")?;
141 let mut defs = reader.vec_with_capacity(len, "index definition")?;
142 for _ in 0..len {
143 defs.push(reader.read_index_definition()?);
144 }
145 reader.finish()?;
146 Ok(defs)
147}
148
149const VALUE_NULL: u8 = 0;
150const VALUE_BOOL: u8 = 1;
151const VALUE_INT: u8 = 2;
152const VALUE_FLOAT: u8 = 3;
153const VALUE_STRING: u8 = 4;
154const VALUE_LIST: u8 = 5;
155const VALUE_MAP: u8 = 6;
156const VALUE_DATE: u8 = 7;
157const VALUE_TIME: u8 = 8;
158const VALUE_LOCAL_TIME: u8 = 9;
159const VALUE_DATE_TIME: u8 = 10;
160const VALUE_LOCAL_DATE_TIME: u8 = 11;
161const VALUE_DURATION: u8 = 12;
162const VALUE_POINT: u8 = 13;
163const VALUE_VECTOR: u8 = 14;
164const VALUE_BINARY: u8 = 15;
165const VALUE_ZONED_DATE_TIME: u8 = 16;
169
170const VECTOR_FLOAT64: u8 = 1;
171const VECTOR_FLOAT32: u8 = 2;
172const VECTOR_INTEGER64: u8 = 3;
173const VECTOR_INTEGER32: u8 = 4;
174const VECTOR_INTEGER16: u8 = 5;
175const VECTOR_INTEGER8: u8 = 6;
176
177const INDEX_KIND_RANGE: u8 = 1;
178const INDEX_KIND_TEXT: u8 = 2;
179const INDEX_KIND_POINT: u8 = 3;
180const INDEX_KIND_LOOKUP: u8 = 4;
181const INDEX_KIND_VECTOR: u8 = 5;
182const INDEX_KIND_FULLTEXT: u8 = 6;
183
184const INDEX_ENTITY_NODE: u8 = 1;
185const INDEX_ENTITY_RELATIONSHIP: u8 = 2;
186
187const INDEX_STATE_ONLINE: u8 = 1;
188const INDEX_STATE_POPULATING: u8 = 2;
189
190const CONFIG_NUMBER: u8 = 1;
191const CONFIG_INTEGER: u8 = 2;
192const CONFIG_STRING: u8 = 3;
193const CONFIG_BOOL: u8 = 4;
194const CONFIG_LIST: u8 = 5;
195const CONFIG_MAP: u8 = 6;
196const CONFIG_NULL: u8 = 7;
197
198const CONSTRAINT_KIND_UNIQUE: u8 = 1;
199const CONSTRAINT_KIND_EXISTENCE: u8 = 2;
200const CONSTRAINT_KIND_NODE_KEY: u8 = 3;
201const CONSTRAINT_KIND_RELATIONSHIP_KEY: u8 = 4;
202const CONSTRAINT_KIND_PROPERTY_TYPE: u8 = 5;
203const CONSTRAINT_BACKING_HASH: u8 = 0x80;
206
207const PROPERTY_TYPE_TERM_SCALAR: u8 = 1;
208const PROPERTY_TYPE_TERM_LIST: u8 = 2;
209const PROPERTY_TYPE_TERM_VECTOR: u8 = 3;
210
211const SCALAR_BOOLEAN: u8 = 1;
212const SCALAR_STRING: u8 = 2;
213const SCALAR_INTEGER: u8 = 3;
214const SCALAR_FLOAT: u8 = 4;
215const SCALAR_DATE: u8 = 5;
216const SCALAR_LOCAL_TIME: u8 = 6;
217const SCALAR_ZONED_TIME: u8 = 7;
218const SCALAR_LOCAL_DATETIME: u8 = 8;
219const SCALAR_ZONED_DATETIME: u8 = 9;
220const SCALAR_DURATION: u8 = 10;
221const SCALAR_POINT: u8 = 11;
222const SCALAR_MAP: u8 = 12;
223const SCALAR_ANY: u8 = 13;
224
225const VCOORD_INT8: u8 = 1;
226const VCOORD_INT16: u8 = 2;
227const VCOORD_INT32: u8 = 3;
228const VCOORD_INT64: u8 = 4;
229const VCOORD_FLOAT32: u8 = 5;
230const VCOORD_FLOAT64: u8 = 6;
231
232fn write_index_request(out: &mut Vec<u8>, request: &IndexRequest) -> Result<()> {
233 write_optional_string(out, request.explicit_name.as_deref())?;
234 write_index_kind(out, request.kind);
235 write_index_entity(out, request.entity);
236 write_optional_string(out, request.label.as_deref())?;
237 write_string_vec(out, &request.additional_labels)?;
238 write_string_vec(out, &request.properties)?;
239 write_config_map(out, &request.options)
240}
241
242fn write_index_definition(out: &mut Vec<u8>, def: &IndexDefinition) -> Result<()> {
243 write_string(out, &def.name)?;
244 write_index_kind(out, def.kind);
245 write_index_entity(out, def.entity);
246 write_optional_string(out, def.label.as_deref())?;
247 write_string_vec(out, &def.additional_labels)?;
248 write_string_vec(out, &def.properties)?;
249 write_config_map(out, &def.options)?;
250 write_index_state(out, def.state);
251 Ok(())
252}
253
254fn write_index_kind(out: &mut Vec<u8>, kind: StoredIndexKind) {
255 out.push(match kind {
256 StoredIndexKind::Range => INDEX_KIND_RANGE,
257 StoredIndexKind::Text => INDEX_KIND_TEXT,
258 StoredIndexKind::Point => INDEX_KIND_POINT,
259 StoredIndexKind::Lookup => INDEX_KIND_LOOKUP,
260 StoredIndexKind::Vector => INDEX_KIND_VECTOR,
261 StoredIndexKind::Fulltext => INDEX_KIND_FULLTEXT,
262 });
263}
264
265fn write_index_entity(out: &mut Vec<u8>, entity: StoredIndexEntity) {
266 out.push(match entity {
267 StoredIndexEntity::Node => INDEX_ENTITY_NODE,
268 StoredIndexEntity::Relationship => INDEX_ENTITY_RELATIONSHIP,
269 });
270}
271
272fn write_index_state(out: &mut Vec<u8>, state: StoredIndexState) {
273 out.push(match state {
274 StoredIndexState::Online => INDEX_STATE_ONLINE,
275 StoredIndexState::Populating => INDEX_STATE_POPULATING,
276 });
277}
278
279fn write_constraint_request(out: &mut Vec<u8>, request: &ConstraintRequest) -> Result<()> {
280 write_string(out, &request.name)?;
281 write_constraint_kind(out, &request.kind, request.backing)?;
282 write_index_entity(out, request.entity);
283 write_string(out, &request.label)?;
284 write_string_vec(out, &request.properties)
285}
286
287fn write_constraint_definition(out: &mut Vec<u8>, def: &ConstraintDefinition) -> Result<()> {
288 write_string(out, &def.name)?;
289 write_constraint_kind(out, &def.kind, def.backing)?;
290 write_index_entity(out, def.entity);
291 write_string(out, &def.label)?;
292 write_string_vec(out, &def.properties)?;
293 write_optional_string(out, def.owned_index.as_deref())
294}
295
296fn write_constraint_kind(
297 out: &mut Vec<u8>,
298 kind: &StoredConstraintKind,
299 backing: ConstraintBacking,
300) -> Result<()> {
301 let hash = match backing {
302 ConstraintBacking::Range => 0,
303 ConstraintBacking::Hash => CONSTRAINT_BACKING_HASH,
304 };
305 match kind {
306 StoredConstraintKind::Unique => out.push(CONSTRAINT_KIND_UNIQUE | hash),
307 StoredConstraintKind::Existence => out.push(CONSTRAINT_KIND_EXISTENCE),
308 StoredConstraintKind::NodeKey => out.push(CONSTRAINT_KIND_NODE_KEY | hash),
309 StoredConstraintKind::RelationshipKey => out.push(CONSTRAINT_KIND_RELATIONSHIP_KEY | hash),
310 StoredConstraintKind::PropertyType(t) => {
311 out.push(CONSTRAINT_KIND_PROPERTY_TYPE);
312 write_property_type(out, t)?;
313 }
314 }
315 Ok(())
316}
317
318fn write_property_type(out: &mut Vec<u8>, t: &StoredPropertyType) -> Result<()> {
319 write_len(out, t.alternatives.len())?;
320 for term in &t.alternatives {
321 write_property_type_term(out, term)?;
322 }
323 Ok(())
324}
325
326fn write_property_type_term(out: &mut Vec<u8>, term: &StoredPropertyTypeTerm) -> Result<()> {
327 match term {
328 StoredPropertyTypeTerm::Scalar(s) => {
329 out.push(PROPERTY_TYPE_TERM_SCALAR);
330 out.push(scalar_tag(*s));
331 }
332 StoredPropertyTypeTerm::List { inner, not_null } => {
333 out.push(PROPERTY_TYPE_TERM_LIST);
334 out.push(u8::from(*not_null));
335 write_property_type_term(out, inner)?;
336 }
337 StoredPropertyTypeTerm::Vector { coord, dimension } => {
338 out.push(PROPERTY_TYPE_TERM_VECTOR);
339 out.push(vector_coord_tag(*coord));
340 write_u32(out, *dimension);
341 }
342 }
343 Ok(())
344}
345
346fn scalar_tag(s: StoredScalarType) -> u8 {
347 match s {
348 StoredScalarType::Boolean => SCALAR_BOOLEAN,
349 StoredScalarType::String => SCALAR_STRING,
350 StoredScalarType::Integer => SCALAR_INTEGER,
351 StoredScalarType::Float => SCALAR_FLOAT,
352 StoredScalarType::Date => SCALAR_DATE,
353 StoredScalarType::LocalTime => SCALAR_LOCAL_TIME,
354 StoredScalarType::ZonedTime => SCALAR_ZONED_TIME,
355 StoredScalarType::LocalDateTime => SCALAR_LOCAL_DATETIME,
356 StoredScalarType::ZonedDateTime => SCALAR_ZONED_DATETIME,
357 StoredScalarType::Duration => SCALAR_DURATION,
358 StoredScalarType::Point => SCALAR_POINT,
359 StoredScalarType::Map => SCALAR_MAP,
360 StoredScalarType::Any => SCALAR_ANY,
361 }
362}
363
364fn vector_coord_tag(c: StoredVectorCoordType) -> u8 {
365 match c {
366 StoredVectorCoordType::Int8 => VCOORD_INT8,
367 StoredVectorCoordType::Int16 => VCOORD_INT16,
368 StoredVectorCoordType::Int32 => VCOORD_INT32,
369 StoredVectorCoordType::Int64 => VCOORD_INT64,
370 StoredVectorCoordType::Float32 => VCOORD_FLOAT32,
371 StoredVectorCoordType::Float64 => VCOORD_FLOAT64,
372 }
373}
374
375fn write_config_map(out: &mut Vec<u8>, values: &BTreeMap<String, IndexConfigValue>) -> Result<()> {
376 write_config_map_at(out, values, 0)
377}
378
379fn write_config_map_at(
380 out: &mut Vec<u8>,
381 values: &BTreeMap<String, IndexConfigValue>,
382 depth: usize,
383) -> Result<()> {
384 write_len(out, values.len())?;
385 for (key, value) in values {
386 write_string(out, key)?;
387 write_config_value(out, value, depth)?;
388 }
389 Ok(())
390}
391
392fn write_config_value(out: &mut Vec<u8>, value: &IndexConfigValue, depth: usize) -> Result<()> {
394 if matches!(value, IndexConfigValue::List(_) | IndexConfigValue::Map(_))
395 && depth >= MAX_VALUE_NESTING
396 {
397 return Err(nesting_error(StoreCodecError::Encode));
398 }
399 match value {
400 IndexConfigValue::Number(value) => {
401 out.push(CONFIG_NUMBER);
402 write_f64(out, *value);
403 }
404 IndexConfigValue::Integer(value) => {
405 out.push(CONFIG_INTEGER);
406 write_i64(out, *value);
407 }
408 IndexConfigValue::String(value) => {
409 out.push(CONFIG_STRING);
410 write_string(out, value)?;
411 }
412 IndexConfigValue::Bool(value) => {
413 out.push(CONFIG_BOOL);
414 out.push(u8::from(*value));
415 }
416 IndexConfigValue::List(values) => {
417 out.push(CONFIG_LIST);
418 write_len(out, values.len())?;
419 for value in values {
420 write_config_value(out, value, depth + 1)?;
421 }
422 }
423 IndexConfigValue::Map(values) => {
424 out.push(CONFIG_MAP);
425 write_config_map_at(out, values, depth + 1)?;
426 }
427 IndexConfigValue::Null => out.push(CONFIG_NULL),
428 }
429 Ok(())
430}
431
432fn write_property_value(
434 out: &mut Vec<u8>,
435 value: &PropertyValue,
436 depth: usize,
437 max_depth: usize,
438) -> Result<()> {
439 if matches!(value, PropertyValue::List(_) | PropertyValue::Map(_)) && depth >= max_depth {
440 return Err(nesting_error(StoreCodecError::Encode));
441 }
442 match value {
443 PropertyValue::Null => out.push(VALUE_NULL),
444 PropertyValue::Bool(value) => {
445 out.push(VALUE_BOOL);
446 out.push(u8::from(*value));
447 }
448 PropertyValue::Int(value) => {
449 out.push(VALUE_INT);
450 write_i64(out, *value);
451 }
452 PropertyValue::Float(value) => {
453 out.push(VALUE_FLOAT);
454 write_f64(out, *value);
455 }
456 PropertyValue::String(value) => {
457 out.push(VALUE_STRING);
458 write_string(out, value)?;
459 }
460 PropertyValue::List(values) => {
461 out.push(VALUE_LIST);
462 write_len(out, values.len())?;
463 for value in values {
464 write_property_value(out, value, depth + 1, max_depth)?;
465 }
466 }
467 PropertyValue::Map(values) => {
468 out.push(VALUE_MAP);
469 write_len(out, values.len())?;
470 for (key, value) in values {
471 write_string(out, key)?;
472 write_property_value(out, value, depth + 1, max_depth)?;
473 }
474 }
475 PropertyValue::Date(value) => {
476 out.push(VALUE_DATE);
477 write_i32(out, value.year);
478 write_u32(out, value.month);
479 write_u32(out, value.day);
480 }
481 PropertyValue::Time(value) => {
482 out.push(VALUE_TIME);
483 write_time_fields(
484 out,
485 value.hour,
486 value.minute,
487 value.second,
488 value.nanosecond,
489 );
490 write_i32(out, value.offset_seconds);
491 }
492 PropertyValue::LocalTime(value) => {
493 out.push(VALUE_LOCAL_TIME);
494 write_time_fields(
495 out,
496 value.hour,
497 value.minute,
498 value.second,
499 value.nanosecond,
500 );
501 }
502 PropertyValue::DateTime(value) => {
503 out.push(if value.zone.is_some() {
504 VALUE_ZONED_DATE_TIME
505 } else {
506 VALUE_DATE_TIME
507 });
508 write_date_fields(out, value.year, value.month, value.day);
509 write_time_fields(
510 out,
511 value.hour,
512 value.minute,
513 value.second,
514 value.nanosecond,
515 );
516 write_i32(out, value.offset_seconds);
517 if let Some(zone) = value.zone {
518 write_string(out, zone.name())?;
519 }
520 }
521 PropertyValue::LocalDateTime(value) => {
522 out.push(VALUE_LOCAL_DATE_TIME);
523 write_date_fields(out, value.year, value.month, value.day);
524 write_time_fields(
525 out,
526 value.hour,
527 value.minute,
528 value.second,
529 value.nanosecond,
530 );
531 }
532 PropertyValue::Duration(value) => {
533 out.push(VALUE_DURATION);
534 write_i64(out, value.months);
535 write_i64(out, value.days);
536 write_i64(out, value.seconds);
537 write_i64(out, value.nanoseconds);
538 }
539 PropertyValue::Point(value) => {
540 out.push(VALUE_POINT);
541 write_f64(out, value.x);
542 write_f64(out, value.y);
543 match value.z {
544 Some(z) => {
545 out.push(1);
546 write_f64(out, z);
547 }
548 None => out.push(0),
549 }
550 write_u32(out, value.srid);
551 }
552 PropertyValue::Vector(value) => {
553 out.push(VALUE_VECTOR);
554 write_vector(out, value)?;
555 }
556 PropertyValue::Binary(value) => {
557 out.push(VALUE_BINARY);
558 write_len(out, value.segments().len())?;
559 for segment in value.segments() {
560 write_bytes(out, segment)?;
561 }
562 }
563 }
564 Ok(())
565}
566
567fn write_date_fields(out: &mut Vec<u8>, year: i32, month: u32, day: u32) {
568 write_i32(out, year);
569 write_u32(out, month);
570 write_u32(out, day);
571}
572
573fn write_time_fields(out: &mut Vec<u8>, hour: u32, minute: u32, second: u32, nanosecond: u32) {
574 write_u32(out, hour);
575 write_u32(out, minute);
576 write_u32(out, second);
577 write_u32(out, nanosecond);
578}
579
580fn write_vector(out: &mut Vec<u8>, vector: &LoraVector) -> Result<()> {
581 write_len(out, vector.values.len())?;
585 match &vector.values {
586 VectorValues::Float64(values) => {
587 out.push(VECTOR_FLOAT64);
588 write_len(out, values.len())?;
589 for value in values {
590 write_f64(out, *value);
591 }
592 }
593 VectorValues::Float32(values) => {
594 out.push(VECTOR_FLOAT32);
595 write_len(out, values.len())?;
596 for value in values {
597 write_f32(out, *value);
598 }
599 }
600 VectorValues::Integer64(values) => {
601 out.push(VECTOR_INTEGER64);
602 write_len(out, values.len())?;
603 for value in values {
604 write_i64(out, *value);
605 }
606 }
607 VectorValues::Integer32(values) => {
608 out.push(VECTOR_INTEGER32);
609 write_len(out, values.len())?;
610 for value in values {
611 write_i32(out, *value);
612 }
613 }
614 VectorValues::Integer16(values) => {
615 out.push(VECTOR_INTEGER16);
616 write_len(out, values.len())?;
617 for value in values {
618 write_i16(out, *value);
619 }
620 }
621 VectorValues::Integer8(values) => {
622 out.push(VECTOR_INTEGER8);
623 write_len(out, values.len())?;
624 for value in values {
625 out.push(*value as u8);
626 }
627 }
628 }
629 Ok(())
630}
631
632fn write_optional_string(out: &mut Vec<u8>, value: Option<&str>) -> Result<()> {
633 match value {
634 Some(value) => {
635 out.push(1);
636 write_string(out, value)?;
637 }
638 None => out.push(0),
639 }
640 Ok(())
641}
642
643fn write_len(out: &mut Vec<u8>, len: usize) -> Result<()> {
644 write_u64(
645 out,
646 u64::try_from(len)
647 .map_err(|_| StoreCodecError::Encode("length does not fit in u64".into()))?,
648 );
649 Ok(())
650}
651
652fn write_bytes(out: &mut Vec<u8>, bytes: &[u8]) -> Result<()> {
653 write_len(out, bytes.len())?;
654 out.extend_from_slice(bytes);
655 Ok(())
656}
657
658fn write_string(out: &mut Vec<u8>, value: &str) -> Result<()> {
659 write_bytes(out, value.as_bytes())
660}
661
662fn write_string_vec(out: &mut Vec<u8>, values: &[String]) -> Result<()> {
663 write_len(out, values.len())?;
664 for value in values {
665 write_string(out, value)?;
666 }
667 Ok(())
668}
669
670fn write_i16(out: &mut Vec<u8>, value: i16) {
671 out.extend_from_slice(&value.to_le_bytes());
672}
673
674fn write_i32(out: &mut Vec<u8>, value: i32) {
675 out.extend_from_slice(&value.to_le_bytes());
676}
677
678fn write_u32(out: &mut Vec<u8>, value: u32) {
679 out.extend_from_slice(&value.to_le_bytes());
680}
681
682fn write_i64(out: &mut Vec<u8>, value: i64) {
683 out.extend_from_slice(&value.to_le_bytes());
684}
685
686fn write_u64(out: &mut Vec<u8>, value: u64) {
687 out.extend_from_slice(&value.to_le_bytes());
688}
689
690fn write_f32(out: &mut Vec<u8>, value: f32) {
691 out.extend_from_slice(&value.to_bits().to_le_bytes());
692}
693
694fn write_f64(out: &mut Vec<u8>, value: f64) {
695 out.extend_from_slice(&value.to_bits().to_le_bytes());
696}
697
698struct Reader<'a> {
699 bytes: &'a [u8],
700 offset: usize,
701 depth: usize,
703 max_depth: usize,
705}
706
707impl<'a> Reader<'a> {
708 fn new(bytes: &'a [u8]) -> Self {
709 Self {
710 bytes,
711 offset: 0,
712 depth: 0,
713 max_depth: MAX_VALUE_NESTING,
714 }
715 }
716
717 fn enter(&mut self) -> Result<()> {
719 if self.depth >= self.max_depth {
720 return Err(nesting_error(StoreCodecError::Decode));
721 }
722 self.depth += 1;
723 Ok(())
724 }
725
726 fn finish(&self) -> Result<()> {
727 if self.offset == self.bytes.len() {
728 Ok(())
729 } else {
730 Err(StoreCodecError::Decode(format!(
731 "trailing bytes: {}",
732 self.bytes.len() - self.offset
733 )))
734 }
735 }
736
737 fn read_exact(&mut self, len: usize) -> Result<&'a [u8]> {
738 let end = self
739 .offset
740 .checked_add(len)
741 .ok_or_else(|| StoreCodecError::Decode("offset overflow".into()))?;
742 if end > self.bytes.len() {
743 return Err(StoreCodecError::Decode("truncated input".into()));
744 }
745 let out = &self.bytes[self.offset..end];
746 self.offset = end;
747 Ok(out)
748 }
749
750 fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
751 self.read_exact(N)?
752 .try_into()
753 .map_err(|_| StoreCodecError::Decode("fixed-width field truncated".into()))
754 }
755
756 fn read_u8(&mut self) -> Result<u8> {
757 Ok(self.read_exact(1)?[0])
758 }
759
760 fn read_i8(&mut self) -> Result<i8> {
761 Ok(self.read_u8()? as i8)
762 }
763
764 fn read_i16(&mut self) -> Result<i16> {
765 Ok(i16::from_le_bytes(self.read_array()?))
766 }
767
768 fn read_i32(&mut self) -> Result<i32> {
769 Ok(i32::from_le_bytes(self.read_array()?))
770 }
771
772 fn read_u32(&mut self) -> Result<u32> {
773 Ok(u32::from_le_bytes(self.read_array()?))
774 }
775
776 fn read_i64(&mut self) -> Result<i64> {
777 Ok(i64::from_le_bytes(self.read_array()?))
778 }
779
780 fn read_u64(&mut self) -> Result<u64> {
781 Ok(u64::from_le_bytes(self.read_array()?))
782 }
783
784 fn read_f32(&mut self) -> Result<f32> {
785 Ok(f32::from_bits(self.read_u32()?))
786 }
787
788 fn read_f64(&mut self) -> Result<f64> {
789 Ok(f64::from_bits(self.read_u64()?))
790 }
791
792 fn read_len(&mut self) -> Result<usize> {
793 usize::try_from(self.read_u64()?)
794 .map_err(|_| StoreCodecError::Decode("length overflows usize".into()))
795 }
796
797 fn remaining(&self) -> usize {
798 self.bytes.len().saturating_sub(self.offset)
799 }
800
801 fn read_len_bounded(&mut self, label: &str) -> Result<usize> {
802 let len = self.read_len()?;
803 if len > self.remaining() {
804 return Err(StoreCodecError::Decode(format!(
805 "{label} count {len} exceeds remaining input"
806 )));
807 }
808 Ok(len)
809 }
810
811 fn vec_with_capacity<T>(&self, len: usize, label: &str) -> Result<Vec<T>> {
812 let mut values = Vec::new();
813 values.try_reserve(len).map_err(|_| {
814 StoreCodecError::Decode(format!("{label} count {len} is too large to allocate"))
815 })?;
816 Ok(values)
817 }
818
819 fn read_bytes(&mut self) -> Result<&'a [u8]> {
820 let len = self.read_len()?;
821 self.read_exact(len)
822 }
823
824 fn read_string(&mut self) -> Result<String> {
825 let bytes = self.read_bytes()?;
826 std::str::from_utf8(bytes)
827 .map(|value| value.to_string())
828 .map_err(|e| StoreCodecError::Decode(format!("invalid UTF-8 string: {e}")))
829 }
830
831 fn read_string_vec(&mut self) -> Result<Vec<String>> {
832 let len = self.read_len_bounded("string")?;
833 let mut values = self.vec_with_capacity(len, "string")?;
834 for _ in 0..len {
835 values.push(self.read_string()?);
836 }
837 Ok(values)
838 }
839
840 fn read_optional_string(&mut self) -> Result<Option<String>> {
841 match self.read_u8()? {
842 0 => Ok(None),
843 1 => Ok(Some(self.read_string()?)),
844 tag => Err(StoreCodecError::Decode(format!(
845 "invalid optional string tag {tag}"
846 ))),
847 }
848 }
849
850 fn read_index_request(&mut self) -> Result<IndexRequest> {
851 Ok(IndexRequest {
852 explicit_name: self.read_optional_string()?,
853 kind: self.read_index_kind()?,
854 entity: self.read_index_entity()?,
855 label: self.read_optional_string()?,
856 additional_labels: self.read_string_vec()?,
857 properties: self.read_string_vec()?,
858 options: self.read_config_map()?,
859 })
860 }
861
862 fn read_index_definition(&mut self) -> Result<IndexDefinition> {
863 Ok(IndexDefinition {
864 name: self.read_string()?,
865 kind: self.read_index_kind()?,
866 entity: self.read_index_entity()?,
867 label: self.read_optional_string()?,
868 additional_labels: self.read_string_vec()?,
869 properties: self.read_string_vec()?,
870 options: self.read_config_map()?,
871 state: self.read_index_state()?,
872 })
873 }
874
875 fn read_index_kind(&mut self) -> Result<StoredIndexKind> {
876 match self.read_u8()? {
877 INDEX_KIND_RANGE => Ok(StoredIndexKind::Range),
878 INDEX_KIND_TEXT => Ok(StoredIndexKind::Text),
879 INDEX_KIND_POINT => Ok(StoredIndexKind::Point),
880 INDEX_KIND_LOOKUP => Ok(StoredIndexKind::Lookup),
881 INDEX_KIND_VECTOR => Ok(StoredIndexKind::Vector),
882 INDEX_KIND_FULLTEXT => Ok(StoredIndexKind::Fulltext),
883 tag => Err(StoreCodecError::Decode(format!(
884 "invalid index kind tag {tag}"
885 ))),
886 }
887 }
888
889 fn read_index_entity(&mut self) -> Result<StoredIndexEntity> {
890 match self.read_u8()? {
891 INDEX_ENTITY_NODE => Ok(StoredIndexEntity::Node),
892 INDEX_ENTITY_RELATIONSHIP => Ok(StoredIndexEntity::Relationship),
893 tag => Err(StoreCodecError::Decode(format!(
894 "invalid index entity tag {tag}"
895 ))),
896 }
897 }
898
899 fn read_index_state(&mut self) -> Result<StoredIndexState> {
900 match self.read_u8()? {
901 INDEX_STATE_ONLINE => Ok(StoredIndexState::Online),
902 INDEX_STATE_POPULATING => Ok(StoredIndexState::Populating),
903 tag => Err(StoreCodecError::Decode(format!(
904 "invalid index state tag {tag}"
905 ))),
906 }
907 }
908
909 fn read_constraint_request(&mut self) -> Result<ConstraintRequest> {
910 let name = self.read_string()?;
911 let (kind, backing) = self.read_constraint_kind()?;
912 Ok(ConstraintRequest {
913 name,
914 kind,
915 entity: self.read_index_entity()?,
916 label: self.read_string()?,
917 properties: self.read_string_vec()?,
918 backing,
919 })
920 }
921
922 fn read_constraint_definition(&mut self) -> Result<ConstraintDefinition> {
923 let name = self.read_string()?;
924 let (kind, backing) = self.read_constraint_kind()?;
925 Ok(ConstraintDefinition {
926 name,
927 kind,
928 entity: self.read_index_entity()?,
929 label: self.read_string()?,
930 properties: self.read_string_vec()?,
931 owned_index: self.read_optional_string()?,
932 backing,
933 })
934 }
935
936 fn read_constraint_kind(&mut self) -> Result<(StoredConstraintKind, ConstraintBacking)> {
937 let tag = self.read_u8()?;
938 let backing = if tag & CONSTRAINT_BACKING_HASH != 0
939 && matches!(
940 tag & !CONSTRAINT_BACKING_HASH,
941 CONSTRAINT_KIND_UNIQUE
942 | CONSTRAINT_KIND_NODE_KEY
943 | CONSTRAINT_KIND_RELATIONSHIP_KEY
944 ) {
945 ConstraintBacking::Hash
946 } else {
947 ConstraintBacking::Range
948 };
949 let tag = match backing {
950 ConstraintBacking::Hash => tag & !CONSTRAINT_BACKING_HASH,
951 ConstraintBacking::Range => tag,
952 };
953 let kind = match tag {
954 CONSTRAINT_KIND_UNIQUE => Ok(StoredConstraintKind::Unique),
955 CONSTRAINT_KIND_EXISTENCE => Ok(StoredConstraintKind::Existence),
956 CONSTRAINT_KIND_NODE_KEY => Ok(StoredConstraintKind::NodeKey),
957 CONSTRAINT_KIND_RELATIONSHIP_KEY => Ok(StoredConstraintKind::RelationshipKey),
958 CONSTRAINT_KIND_PROPERTY_TYPE => Ok(StoredConstraintKind::PropertyType(
959 self.read_property_type()?,
960 )),
961 tag => Err(StoreCodecError::Decode(format!(
962 "invalid constraint kind tag {tag}"
963 ))),
964 }?;
965 Ok((kind, backing))
966 }
967
968 fn read_property_type(&mut self) -> Result<StoredPropertyType> {
969 let len = self.read_len_bounded("property type alternative")?;
970 let mut alternatives = self.vec_with_capacity(len, "property type alternative")?;
971 for _ in 0..len {
972 alternatives.push(self.read_property_type_term()?);
973 }
974 Ok(StoredPropertyType { alternatives })
975 }
976
977 fn read_property_type_term(&mut self) -> Result<StoredPropertyTypeTerm> {
978 match self.read_u8()? {
979 PROPERTY_TYPE_TERM_SCALAR => {
980 Ok(StoredPropertyTypeTerm::Scalar(self.read_scalar_type()?))
981 }
982 PROPERTY_TYPE_TERM_LIST => {
983 let not_null = self.read_u8()? != 0;
984 let inner = Box::new(self.read_property_type_term()?);
985 Ok(StoredPropertyTypeTerm::List { inner, not_null })
986 }
987 PROPERTY_TYPE_TERM_VECTOR => {
988 let coord = self.read_vector_coord_type()?;
989 let dimension = self.read_u32()?;
990 Ok(StoredPropertyTypeTerm::Vector { coord, dimension })
991 }
992 tag => Err(StoreCodecError::Decode(format!(
993 "invalid property-type term tag {tag}"
994 ))),
995 }
996 }
997
998 fn read_scalar_type(&mut self) -> Result<StoredScalarType> {
999 match self.read_u8()? {
1000 SCALAR_BOOLEAN => Ok(StoredScalarType::Boolean),
1001 SCALAR_STRING => Ok(StoredScalarType::String),
1002 SCALAR_INTEGER => Ok(StoredScalarType::Integer),
1003 SCALAR_FLOAT => Ok(StoredScalarType::Float),
1004 SCALAR_DATE => Ok(StoredScalarType::Date),
1005 SCALAR_LOCAL_TIME => Ok(StoredScalarType::LocalTime),
1006 SCALAR_ZONED_TIME => Ok(StoredScalarType::ZonedTime),
1007 SCALAR_LOCAL_DATETIME => Ok(StoredScalarType::LocalDateTime),
1008 SCALAR_ZONED_DATETIME => Ok(StoredScalarType::ZonedDateTime),
1009 SCALAR_DURATION => Ok(StoredScalarType::Duration),
1010 SCALAR_POINT => Ok(StoredScalarType::Point),
1011 SCALAR_MAP => Ok(StoredScalarType::Map),
1012 SCALAR_ANY => Ok(StoredScalarType::Any),
1013 tag => Err(StoreCodecError::Decode(format!(
1014 "invalid scalar type tag {tag}"
1015 ))),
1016 }
1017 }
1018
1019 fn read_vector_coord_type(&mut self) -> Result<StoredVectorCoordType> {
1020 match self.read_u8()? {
1021 VCOORD_INT8 => Ok(StoredVectorCoordType::Int8),
1022 VCOORD_INT16 => Ok(StoredVectorCoordType::Int16),
1023 VCOORD_INT32 => Ok(StoredVectorCoordType::Int32),
1024 VCOORD_INT64 => Ok(StoredVectorCoordType::Int64),
1025 VCOORD_FLOAT32 => Ok(StoredVectorCoordType::Float32),
1026 VCOORD_FLOAT64 => Ok(StoredVectorCoordType::Float64),
1027 tag => Err(StoreCodecError::Decode(format!(
1028 "invalid vector coord type tag {tag}"
1029 ))),
1030 }
1031 }
1032
1033 fn read_config_map(&mut self) -> Result<BTreeMap<String, IndexConfigValue>> {
1034 let len = self.read_len_bounded("index config map entry")?;
1035 let mut values = BTreeMap::new();
1036 for _ in 0..len {
1037 values.insert(self.read_string()?, self.read_config_value()?);
1038 }
1039 Ok(values)
1040 }
1041
1042 fn read_config_value(&mut self) -> Result<IndexConfigValue> {
1043 Ok(match self.read_u8()? {
1044 CONFIG_NUMBER => IndexConfigValue::Number(self.read_f64()?),
1045 CONFIG_INTEGER => IndexConfigValue::Integer(self.read_i64()?),
1046 CONFIG_STRING => IndexConfigValue::String(self.read_string()?),
1047 CONFIG_BOOL => IndexConfigValue::Bool(self.read_u8()? != 0),
1048 CONFIG_LIST => {
1049 self.enter()?;
1050 let len = self.read_len_bounded("index config list value")?;
1051 let mut values = self.vec_with_capacity(len, "index config list value")?;
1052 for _ in 0..len {
1053 values.push(self.read_config_value()?);
1054 }
1055 self.depth -= 1;
1056 IndexConfigValue::List(values)
1057 }
1058 CONFIG_MAP => {
1059 self.enter()?;
1060 let values = self.read_config_map()?;
1061 self.depth -= 1;
1062 IndexConfigValue::Map(values)
1063 }
1064 CONFIG_NULL => IndexConfigValue::Null,
1065 tag => {
1066 return Err(StoreCodecError::Decode(format!(
1067 "invalid index config value tag {tag}"
1068 )));
1069 }
1070 })
1071 }
1072
1073 fn read_property_value(&mut self) -> Result<PropertyValue> {
1074 Ok(match self.read_u8()? {
1075 VALUE_NULL => PropertyValue::Null,
1076 VALUE_BOOL => PropertyValue::Bool(self.read_u8()? != 0),
1077 VALUE_INT => PropertyValue::Int(self.read_i64()?),
1078 VALUE_FLOAT => PropertyValue::Float(self.read_f64()?),
1079 VALUE_STRING => PropertyValue::String(self.read_string()?),
1080 VALUE_LIST => {
1081 self.enter()?;
1082 let len = self.read_len_bounded("property list value")?;
1083 let mut values = self.vec_with_capacity(len, "property list value")?;
1084 for _ in 0..len {
1085 values.push(self.read_property_value()?);
1086 }
1087 self.depth -= 1;
1088 PropertyValue::List(values)
1089 }
1090 VALUE_MAP => {
1091 self.enter()?;
1092 let len = self.read_len_bounded("property map entry")?;
1093 let mut values = BTreeMap::new();
1094 for _ in 0..len {
1095 values.insert(self.read_string()?, self.read_property_value()?);
1096 }
1097 self.depth -= 1;
1098 PropertyValue::Map(values)
1099 }
1100 VALUE_DATE => PropertyValue::Date(LoraDate {
1101 year: self.read_i32()?,
1102 month: self.read_u32()?,
1103 day: self.read_u32()?,
1104 }),
1105 VALUE_TIME => PropertyValue::Time(LoraTime {
1106 hour: self.read_u32()?,
1107 minute: self.read_u32()?,
1108 second: self.read_u32()?,
1109 nanosecond: self.read_u32()?,
1110 offset_seconds: self.read_i32()?,
1111 }),
1112 VALUE_LOCAL_TIME => PropertyValue::LocalTime(LoraLocalTime {
1113 hour: self.read_u32()?,
1114 minute: self.read_u32()?,
1115 second: self.read_u32()?,
1116 nanosecond: self.read_u32()?,
1117 }),
1118 tag @ (VALUE_DATE_TIME | VALUE_ZONED_DATE_TIME) => {
1119 let mut value = LoraDateTime {
1120 year: self.read_i32()?,
1121 month: self.read_u32()?,
1122 day: self.read_u32()?,
1123 hour: self.read_u32()?,
1124 minute: self.read_u32()?,
1125 second: self.read_u32()?,
1126 nanosecond: self.read_u32()?,
1127 offset_seconds: self.read_i32()?,
1128 zone: None,
1129 };
1130 if tag == VALUE_ZONED_DATE_TIME {
1131 let name = self.read_string()?;
1132 value.zone = Some(ZoneId::lookup(&name).ok_or_else(|| {
1133 StoreCodecError::Decode(format!("unknown time zone `{name}`"))
1134 })?);
1135 }
1136 PropertyValue::DateTime(value)
1137 }
1138 VALUE_LOCAL_DATE_TIME => PropertyValue::LocalDateTime(LoraLocalDateTime {
1139 year: self.read_i32()?,
1140 month: self.read_u32()?,
1141 day: self.read_u32()?,
1142 hour: self.read_u32()?,
1143 minute: self.read_u32()?,
1144 second: self.read_u32()?,
1145 nanosecond: self.read_u32()?,
1146 }),
1147 VALUE_DURATION => PropertyValue::Duration(LoraDuration {
1148 months: self.read_i64()?,
1149 days: self.read_i64()?,
1150 seconds: self.read_i64()?,
1151 nanoseconds: self.read_i64()?,
1152 }),
1153 VALUE_POINT => {
1154 let x = self.read_f64()?;
1155 let y = self.read_f64()?;
1156 let z = match self.read_u8()? {
1157 0 => None,
1158 1 => Some(self.read_f64()?),
1159 tag => {
1160 return Err(StoreCodecError::Decode(format!(
1161 "invalid point z-presence tag {tag}"
1162 )));
1163 }
1164 };
1165 PropertyValue::Point(LoraPoint {
1166 x,
1167 y,
1168 z,
1169 srid: self.read_u32()?,
1170 })
1171 }
1172 VALUE_VECTOR => PropertyValue::Vector(self.read_vector()?),
1173 VALUE_BINARY => PropertyValue::Binary(self.read_binary()?),
1174 tag => {
1175 return Err(StoreCodecError::Decode(format!(
1176 "invalid property value tag {tag}"
1177 )));
1178 }
1179 })
1180 }
1181
1182 fn read_vector(&mut self) -> Result<LoraVector> {
1183 let dimension = self.read_len()?;
1184 let values = match self.read_u8()? {
1185 VECTOR_FLOAT64 => {
1186 read_vec(self, |reader| reader.read_f64()).map(VectorValues::Float64)?
1187 }
1188 VECTOR_FLOAT32 => {
1189 read_vec(self, |reader| reader.read_f32()).map(VectorValues::Float32)?
1190 }
1191 VECTOR_INTEGER64 => {
1192 read_vec(self, |reader| reader.read_i64()).map(VectorValues::Integer64)?
1193 }
1194 VECTOR_INTEGER32 => {
1195 read_vec(self, |reader| reader.read_i32()).map(VectorValues::Integer32)?
1196 }
1197 VECTOR_INTEGER16 => {
1198 read_vec(self, |reader| reader.read_i16()).map(VectorValues::Integer16)?
1199 }
1200 VECTOR_INTEGER8 => {
1201 read_vec(self, |reader| reader.read_i8()).map(VectorValues::Integer8)?
1202 }
1203 tag => {
1204 return Err(StoreCodecError::Decode(format!(
1205 "unknown vector value tag {tag}"
1206 )))
1207 }
1208 };
1209 if values.len() != dimension {
1210 return Err(StoreCodecError::Decode(format!(
1211 "vector dimension mismatch: declared {dimension}, got {}",
1212 values.len()
1213 )));
1214 }
1215 Ok(LoraVector { dimension, values })
1216 }
1217
1218 fn read_binary(&mut self) -> Result<LoraBinary> {
1219 let len = self.read_len_bounded("binary segment")?;
1220 let mut segments = self.vec_with_capacity(len, "binary segment")?;
1221 for _ in 0..len {
1222 segments.push(self.read_bytes()?.to_vec());
1223 }
1224 Ok(LoraBinary::from_segments(segments))
1225 }
1226}
1227
1228fn read_vec<T>(
1229 reader: &mut Reader<'_>,
1230 mut read_one: impl FnMut(&mut Reader<'_>) -> Result<T>,
1231) -> Result<Vec<T>> {
1232 let len = reader.read_len()?;
1233 if len > reader.remaining() {
1234 return Err(StoreCodecError::Decode(format!(
1235 "vector value count {len} exceeds remaining input"
1236 )));
1237 }
1238 let mut values = reader.vec_with_capacity(len, "vector value")?;
1239 for _ in 0..len {
1240 values.push(read_one(reader)?);
1241 }
1242 Ok(values)
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247 use super::*;
1248 use crate::{IndexConfigValue, StoredIndexEntity, StoredIndexKind};
1249
1250 #[test]
1253 fn constraint_backing_round_trips_and_old_records_read_as_range() {
1254 for (kind, backing) in [
1255 (StoredConstraintKind::Unique, ConstraintBacking::Hash),
1256 (StoredConstraintKind::NodeKey, ConstraintBacking::Hash),
1257 (
1258 StoredConstraintKind::RelationshipKey,
1259 ConstraintBacking::Hash,
1260 ),
1261 (StoredConstraintKind::Unique, ConstraintBacking::Range),
1262 (StoredConstraintKind::Existence, ConstraintBacking::Range),
1263 ] {
1264 let request = ConstraintRequest {
1265 name: "c".into(),
1266 kind: kind.clone(),
1267 entity: StoredIndexEntity::Node,
1268 label: "R".into(),
1269 properties: vec!["k".into()],
1270 backing,
1271 };
1272 let bytes = encode_constraint_request(&request).unwrap();
1273 assert_eq!(decode_constraint_request(&bytes).unwrap(), request);
1274 let def = ConstraintDefinition {
1275 name: "c".into(),
1276 kind,
1277 entity: StoredIndexEntity::Node,
1278 label: "R".into(),
1279 properties: vec!["k".into()],
1280 owned_index: Some("c".into()),
1281 backing,
1282 };
1283 let bytes = encode_constraint_definitions(std::slice::from_ref(&def)).unwrap();
1284 assert_eq!(decode_constraint_definitions(&bytes).unwrap(), vec![def]);
1285 }
1286 let mut old = Vec::new();
1288 write_string(&mut old, "c").unwrap();
1289 old.push(CONSTRAINT_KIND_UNIQUE);
1290 write_index_entity(&mut old, StoredIndexEntity::Node);
1291 write_string(&mut old, "R").unwrap();
1292 write_string_vec(&mut old, &["k".to_string()]).unwrap();
1293 let read = decode_constraint_request(&old).unwrap();
1294 assert_eq!(read.kind, StoredConstraintKind::Unique);
1295 assert_eq!(read.backing, ConstraintBacking::Range);
1296 }
1297
1298 fn nested_list(depth: usize) -> PropertyValue {
1299 (0..depth).fold(PropertyValue::Null, |inner, _| {
1300 PropertyValue::List(vec![inner])
1301 })
1302 }
1303
1304 fn nested_bytes(depth: usize, tag: u8, null: u8, map: bool) -> Vec<u8> {
1307 let mut out = Vec::new();
1308 for _ in 0..depth {
1309 out.push(tag);
1310 write_len(&mut out, 1).unwrap();
1311 if map {
1312 write_string(&mut out, "k").unwrap();
1313 }
1314 }
1315 out.push(null);
1316 out
1317 }
1318
1319 #[test]
1323 fn nesting_is_bounded_on_decode_and_encode() {
1324 for map in [false, true] {
1325 let tag = if map { VALUE_MAP } else { VALUE_LIST };
1326 let err =
1327 decode_property_value(&nested_bytes(100_000, tag, VALUE_NULL, map)).unwrap_err();
1328 assert!(err.to_string().contains("more than 128 deep"), "{err}");
1329 assert!(
1330 decode_property_value(&nested_bytes(MAX_VALUE_NESTING, tag, VALUE_NULL, map))
1331 .is_ok()
1332 );
1333 assert!(decode_property_value(&nested_bytes(
1334 MAX_VALUE_NESTING + 1,
1335 tag,
1336 VALUE_NULL,
1337 map
1338 ))
1339 .is_err());
1340 }
1341 let deepest = nested_list(MAX_VALUE_NESTING);
1342 let bytes = encode_property_value(&deepest).unwrap();
1343 assert_eq!(decode_property_value(&bytes).unwrap(), deepest);
1344 let err = encode_property_value(&nested_list(MAX_VALUE_NESTING + 1)).unwrap_err();
1345 assert!(matches!(err, StoreCodecError::Encode(_)), "{err}");
1346
1347 for map in [false, true] {
1348 let tag = if map { CONFIG_MAP } else { CONFIG_LIST };
1349 let mut reader = Reader::new(&[]);
1350 let bytes = nested_bytes(100_000, tag, CONFIG_NULL, map);
1351 reader.bytes = &bytes;
1352 assert!(reader.read_config_value().is_err());
1353 }
1354 let config = (0..MAX_VALUE_NESTING + 1).fold(IndexConfigValue::Null, |inner, _| {
1355 IndexConfigValue::List(vec![inner])
1356 });
1357 let mut out = Vec::new();
1358 assert!(write_config_value(&mut out, &config, 0).is_err());
1359 }
1360
1361 #[test]
1365 fn stored_values_nest_without_a_limit() {
1366 let deep = nested_list(MAX_VALUE_NESTING + 50);
1367 let bytes = encode_stored_property_value(&deep).unwrap();
1368 assert_eq!(decode_stored_property_value(&bytes).unwrap(), deep);
1369
1370 let mut g = crate::InMemoryGraph::new();
1371 let mut props = crate::Properties::new();
1372 props.insert(crate::intern("deep"), deep.clone());
1373 let id = crate::GraphStorageMut::create_node(&mut g, vec!["N".into()], props).id;
1374 assert_eq!(
1375 crate::GraphStorage::node_property(&g, id, "deep"),
1376 Some(deep)
1377 );
1378 }
1379
1380 #[test]
1381 fn property_value_roundtrips_nested_values() {
1382 let value = PropertyValue::Map(BTreeMap::from([
1383 ("name".into(), PropertyValue::String("Ada".into())),
1384 (
1385 "scores".into(),
1386 PropertyValue::List(vec![PropertyValue::Int(1), PropertyValue::Float(-2.5)]),
1387 ),
1388 ]));
1389
1390 let bytes = encode_property_value(&value).unwrap();
1391 assert_eq!(decode_property_value(&bytes).unwrap(), value);
1392 }
1393
1394 #[test]
1395 fn datetimes_roundtrip_with_and_without_a_zone() {
1396 let fixed = LoraDateTime::parse("2026-07-01T12:00:00+02:00").unwrap();
1397 let zoned = LoraDateTime::parse("2026-07-01T12:00:00+02:00[Europe/Amsterdam]").unwrap();
1398 for value in [fixed.clone(), zoned] {
1399 let value = PropertyValue::DateTime(value);
1400 let bytes = encode_property_value(&value).unwrap();
1401 assert_eq!(decode_property_value(&bytes).unwrap(), value);
1402 }
1403 let bytes = encode_property_value(&PropertyValue::DateTime(fixed)).unwrap();
1406 assert_eq!(bytes[0], VALUE_DATE_TIME);
1407 assert_eq!(bytes.len(), 1 + 8 * 4);
1408 let mut unknown = vec![VALUE_ZONED_DATE_TIME];
1409 unknown.extend_from_slice(&bytes[1..]);
1410 write_string(&mut unknown, "Mars/Olympus").unwrap();
1411 assert!(decode_property_value(&unknown)
1412 .unwrap_err()
1413 .to_string()
1414 .contains("Mars/Olympus"));
1415 }
1416
1417 #[test]
1418 fn property_value_rejects_oversized_list_length() {
1419 let mut bytes = vec![VALUE_LIST];
1420 bytes.extend_from_slice(&u64::MAX.to_le_bytes());
1421
1422 let err = decode_property_value(&bytes).unwrap_err();
1423 assert!(matches!(err, StoreCodecError::Decode(_)));
1424 assert!(err.to_string().contains("exceeds remaining input"));
1425 }
1426
1427 #[test]
1428 fn index_request_roundtrips_options() {
1429 let request = IndexRequest {
1430 explicit_name: Some("idx_person_name".into()),
1431 kind: StoredIndexKind::Text,
1432 entity: StoredIndexEntity::Node,
1433 label: Some("Person".into()),
1434 additional_labels: Vec::new(),
1435 properties: vec!["name".into()],
1436 options: BTreeMap::from([(
1437 "indexConfig".into(),
1438 IndexConfigValue::Map(BTreeMap::from([(
1439 "trigram.min".into(),
1440 IndexConfigValue::Integer(3),
1441 )])),
1442 )]),
1443 };
1444
1445 let bytes = encode_index_request(&request).unwrap();
1446 assert_eq!(decode_index_request(&bytes).unwrap(), request);
1447 }
1448
1449 #[test]
1450 fn index_definition_vec_roundtrips_state() {
1451 let defs = vec![IndexDefinition {
1452 name: "idx_person_age".into(),
1453 kind: StoredIndexKind::Range,
1454 entity: StoredIndexEntity::Node,
1455 label: Some("Person".into()),
1456 additional_labels: Vec::new(),
1457 properties: vec!["age".into()],
1458 options: BTreeMap::new(),
1459 state: StoredIndexState::Online,
1460 }];
1461
1462 let bytes = encode_index_definitions(&defs).unwrap();
1463 assert_eq!(decode_index_definitions(&bytes).unwrap(), defs);
1464 }
1465}