1use arcstr::ArcStr;
8use serde::{Deserialize, Serialize};
9use std::collections::BTreeMap;
10use std::fmt;
11use std::hash::{Hash, Hasher};
12use std::sync::Arc;
13
14use super::{Date, Duration, Time, Timestamp, ZonedDatetime};
15
16#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
21pub struct PropertyKey(ArcStr);
22
23impl PropertyKey {
24 #[must_use]
26 pub fn new(s: impl Into<ArcStr>) -> Self {
27 Self(s.into())
28 }
29
30 #[must_use]
32 pub fn as_str(&self) -> &str {
33 &self.0
34 }
35}
36
37impl fmt::Debug for PropertyKey {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 write!(f, "PropertyKey({:?})", self.0)
40 }
41}
42
43impl fmt::Display for PropertyKey {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{}", self.0)
46 }
47}
48
49impl From<&str> for PropertyKey {
50 fn from(s: &str) -> Self {
51 Self::new(s)
52 }
53}
54
55impl From<String> for PropertyKey {
56 fn from(s: String) -> Self {
57 Self::new(s)
58 }
59}
60
61impl AsRef<str> for PropertyKey {
62 fn as_ref(&self) -> &str {
63 &self.0
64 }
65}
66
67impl std::borrow::Borrow<str> for PropertyKey {
68 fn borrow(&self) -> &str {
69 &self.0
70 }
71}
72
73#[derive(Clone, PartialEq, Serialize, Deserialize)]
93#[non_exhaustive]
94pub enum Value {
95 Null,
97
98 Bool(bool),
100
101 Int64(i64),
103
104 Float64(f64),
106
107 String(ArcStr),
109
110 Bytes(Arc<[u8]>),
112
113 Timestamp(Timestamp),
115
116 Date(Date),
118
119 Time(Time),
121
122 Duration(Duration),
124
125 ZonedDatetime(ZonedDatetime),
127
128 List(Arc<[Value]>),
130
131 Map(Arc<BTreeMap<PropertyKey, Value>>),
133
134 Vector(Arc<[f32]>),
139
140 Path {
146 nodes: Arc<[Value]>,
148 edges: Arc<[Value]>,
150 },
151
152 GCounter(Arc<std::collections::HashMap<String, u64>>),
161
162 OnCounter {
169 pos: Arc<std::collections::HashMap<String, u64>>,
171 neg: Arc<std::collections::HashMap<String, u64>>,
174 },
175}
176
177impl Value {
178 #[inline]
180 #[must_use]
181 pub const fn is_null(&self) -> bool {
182 matches!(self, Value::Null)
183 }
184
185 #[inline]
187 #[must_use]
188 pub const fn as_bool(&self) -> Option<bool> {
189 match self {
190 Value::Bool(b) => Some(*b),
191 _ => None,
192 }
193 }
194
195 #[inline]
197 #[must_use]
198 pub const fn as_int64(&self) -> Option<i64> {
199 match self {
200 Value::Int64(i) => Some(*i),
201 _ => None,
202 }
203 }
204
205 #[inline]
207 #[must_use]
208 pub const fn as_float64(&self) -> Option<f64> {
209 match self {
210 Value::Float64(f) => Some(*f),
211 _ => None,
212 }
213 }
214
215 #[inline]
217 #[must_use]
218 pub fn as_str(&self) -> Option<&str> {
219 match self {
220 Value::String(s) => Some(s),
221 _ => None,
222 }
223 }
224
225 #[inline]
227 #[must_use]
228 pub fn as_bytes(&self) -> Option<&[u8]> {
229 match self {
230 Value::Bytes(b) => Some(b),
231 _ => None,
232 }
233 }
234
235 #[inline]
237 #[must_use]
238 pub const fn as_timestamp(&self) -> Option<Timestamp> {
239 match self {
240 Value::Timestamp(t) => Some(*t),
241 _ => None,
242 }
243 }
244
245 #[inline]
247 #[must_use]
248 pub const fn as_date(&self) -> Option<Date> {
249 match self {
250 Value::Date(d) => Some(*d),
251 _ => None,
252 }
253 }
254
255 #[inline]
257 #[must_use]
258 pub const fn as_time(&self) -> Option<Time> {
259 match self {
260 Value::Time(t) => Some(*t),
261 _ => None,
262 }
263 }
264
265 #[inline]
267 #[must_use]
268 pub const fn as_duration(&self) -> Option<Duration> {
269 match self {
270 Value::Duration(d) => Some(*d),
271 _ => None,
272 }
273 }
274
275 #[inline]
277 #[must_use]
278 pub const fn as_zoned_datetime(&self) -> Option<ZonedDatetime> {
279 match self {
280 Value::ZonedDatetime(zdt) => Some(*zdt),
281 _ => None,
282 }
283 }
284
285 #[inline]
287 #[must_use]
288 pub fn as_list(&self) -> Option<&[Value]> {
289 match self {
290 Value::List(l) => Some(l),
291 _ => None,
292 }
293 }
294
295 #[inline]
297 #[must_use]
298 pub fn as_map(&self) -> Option<&BTreeMap<PropertyKey, Value>> {
299 match self {
300 Value::Map(m) => Some(m),
301 _ => None,
302 }
303 }
304
305 #[inline]
307 #[must_use]
308 pub fn as_vector(&self) -> Option<&[f32]> {
309 match self {
310 Value::Vector(v) => Some(v),
311 _ => None,
312 }
313 }
314
315 #[inline]
317 #[must_use]
318 pub fn as_path(&self) -> Option<(&[Value], &[Value])> {
319 match self {
320 Value::Path { nodes, edges } => Some((nodes, edges)),
321 _ => None,
322 }
323 }
324
325 #[inline]
327 #[must_use]
328 pub const fn is_vector(&self) -> bool {
329 matches!(self, Value::Vector(_))
330 }
331
332 #[inline]
334 #[must_use]
335 pub fn vector_dimensions(&self) -> Option<usize> {
336 match self {
337 Value::Vector(v) => Some(v.len()),
338 _ => None,
339 }
340 }
341
342 #[must_use]
344 pub const fn type_name(&self) -> &'static str {
345 match self {
346 Value::Null => "NULL",
347 Value::Bool(_) => "BOOL",
348 Value::Int64(_) => "INT64",
349 Value::Float64(_) => "FLOAT64",
350 Value::String(_) => "STRING",
351 Value::Bytes(_) => "BYTES",
352 Value::Timestamp(_) => "TIMESTAMP",
353 Value::Date(_) => "DATE",
354 Value::Time(_) => "TIME",
355 Value::Duration(_) => "DURATION",
356 Value::ZonedDatetime(_) => "ZONED DATETIME",
357 Value::List(_) => "LIST",
358 Value::Map(_) => "MAP",
359 Value::Vector(_) => "VECTOR",
360 Value::Path { .. } => "PATH",
361 Value::GCounter(_) => "GCOUNTER",
362 Value::OnCounter { .. } => "PNCOUNTER",
363 }
364 }
365
366 pub fn serialize(&self) -> Result<Vec<u8>, bincode::error::EncodeError> {
372 bincode::serde::encode_to_vec(self, bincode::config::standard())
373 }
374
375 pub fn deserialize(bytes: &[u8]) -> Result<Self, bincode::error::DecodeError> {
381 let (value, _) = bincode::serde::decode_from_slice(bytes, bincode::config::standard())?;
382 Ok(value)
383 }
384}
385
386impl fmt::Debug for Value {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 match self {
389 Value::Null => write!(f, "Null"),
390 Value::Bool(b) => write!(f, "Bool({b})"),
391 Value::Int64(i) => write!(f, "Int64({i})"),
392 Value::Float64(fl) => write!(f, "Float64({fl})"),
393 Value::String(s) => write!(f, "String({s:?})"),
394 Value::Bytes(b) => write!(f, "Bytes([{}; {} bytes])", b.first().unwrap_or(&0), b.len()),
395 Value::Timestamp(t) => write!(f, "Timestamp({t:?})"),
396 Value::Date(d) => write!(f, "Date({d})"),
397 Value::Time(t) => write!(f, "Time({t})"),
398 Value::Duration(d) => write!(f, "Duration({d})"),
399 Value::ZonedDatetime(zdt) => write!(f, "ZonedDatetime({zdt})"),
400 Value::List(l) => write!(f, "List({l:?})"),
401 Value::Map(m) => write!(f, "Map({m:?})"),
402 Value::Vector(v) => write!(
403 f,
404 "Vector([{}; {} dims])",
405 v.first().unwrap_or(&0.0),
406 v.len()
407 ),
408 Value::Path { nodes, edges } => {
409 write!(f, "Path({} nodes, {} edges)", nodes.len(), edges.len())
410 }
411 Value::GCounter(counts) => {
412 let total: u64 = counts.values().sum();
413 write!(f, "GCounter(total={total}, replicas={})", counts.len())
414 }
415 Value::OnCounter { pos, neg } => {
416 let pos_sum: i64 = pos.values().copied().map(|v| v as i64).sum();
417 let neg_sum: i64 = neg.values().copied().map(|v| v as i64).sum();
418 write!(f, "OnCounter(net={})", pos_sum - neg_sum)
419 }
420 }
421 }
422}
423
424impl fmt::Display for Value {
425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 match self {
427 Value::Null => write!(f, "NULL"),
428 Value::Bool(b) => write!(f, "{b}"),
429 Value::Int64(i) => write!(f, "{i}"),
430 Value::Float64(fl) => write!(f, "{fl}"),
431 Value::String(s) => write!(f, "{s:?}"),
432 Value::Bytes(b) => write!(f, "<bytes: {} bytes>", b.len()),
433 Value::Timestamp(t) => write!(f, "{t}"),
434 Value::Date(d) => write!(f, "{d}"),
435 Value::Time(t) => write!(f, "{t}"),
436 Value::Duration(d) => write!(f, "{d}"),
437 Value::ZonedDatetime(zdt) => write!(f, "{zdt}"),
438 Value::List(l) => {
439 write!(f, "[")?;
440 for (i, v) in l.iter().enumerate() {
441 if i > 0 {
442 write!(f, ", ")?;
443 }
444 write!(f, "{v}")?;
445 }
446 write!(f, "]")
447 }
448 Value::Map(m) => {
449 write!(f, "{{")?;
450 for (i, (k, v)) in m.iter().enumerate() {
451 if i > 0 {
452 write!(f, ", ")?;
453 }
454 write!(f, "{k}: {v}")?;
455 }
456 write!(f, "}}")
457 }
458 Value::Vector(v) => {
459 write!(f, "vector([")?;
460 let show_count = v.len().min(3);
461 for (i, val) in v.iter().take(show_count).enumerate() {
462 if i > 0 {
463 write!(f, ", ")?;
464 }
465 write!(f, "{val}")?;
466 }
467 if v.len() > 3 {
468 write!(f, ", ... ({} dims)", v.len())?;
469 }
470 write!(f, "])")
471 }
472 Value::Path { nodes, edges } => {
473 write!(f, "<")?;
475 for (i, node) in nodes.iter().enumerate() {
476 if i > 0
477 && let Some(edge) = edges.get(i - 1)
478 {
479 write!(f, "-[{edge}]-")?;
480 }
481 write!(f, "({node})")?;
482 }
483 write!(f, ">")
484 }
485 Value::GCounter(counts) => {
486 let total: u64 = counts.values().sum();
487 write!(f, "GCounter({total})")
488 }
489 Value::OnCounter { pos, neg } => {
490 let pos_sum: i64 = pos.values().copied().map(|v| v as i64).sum();
491 let neg_sum: i64 = neg.values().copied().map(|v| v as i64).sum();
492 write!(f, "OnCounter({})", pos_sum - neg_sum)
493 }
494 }
495 }
496}
497
498impl From<bool> for Value {
500 fn from(b: bool) -> Self {
501 Value::Bool(b)
502 }
503}
504
505impl From<i64> for Value {
506 fn from(i: i64) -> Self {
507 Value::Int64(i)
508 }
509}
510
511impl From<i32> for Value {
512 fn from(i: i32) -> Self {
513 Value::Int64(i64::from(i))
514 }
515}
516
517impl From<f64> for Value {
518 fn from(f: f64) -> Self {
519 Value::Float64(f)
520 }
521}
522
523impl From<f32> for Value {
524 fn from(f: f32) -> Self {
525 Value::Float64(f64::from(f))
526 }
527}
528
529impl From<&str> for Value {
530 fn from(s: &str) -> Self {
531 Value::String(s.into())
532 }
533}
534
535impl From<String> for Value {
536 fn from(s: String) -> Self {
537 Value::String(s.into())
538 }
539}
540
541impl From<ArcStr> for Value {
542 fn from(s: ArcStr) -> Self {
543 Value::String(s)
544 }
545}
546
547impl From<Vec<u8>> for Value {
548 fn from(b: Vec<u8>) -> Self {
549 Value::Bytes(b.into())
550 }
551}
552
553impl From<&[u8]> for Value {
554 fn from(b: &[u8]) -> Self {
555 Value::Bytes(b.into())
556 }
557}
558
559impl From<Timestamp> for Value {
560 fn from(t: Timestamp) -> Self {
561 Value::Timestamp(t)
562 }
563}
564
565impl From<Date> for Value {
566 fn from(d: Date) -> Self {
567 Value::Date(d)
568 }
569}
570
571impl From<Time> for Value {
572 fn from(t: Time) -> Self {
573 Value::Time(t)
574 }
575}
576
577impl From<Duration> for Value {
578 fn from(d: Duration) -> Self {
579 Value::Duration(d)
580 }
581}
582
583impl From<ZonedDatetime> for Value {
584 fn from(zdt: ZonedDatetime) -> Self {
585 Value::ZonedDatetime(zdt)
586 }
587}
588
589impl<T: Into<Value>> From<Vec<T>> for Value {
590 fn from(v: Vec<T>) -> Self {
591 Value::List(v.into_iter().map(Into::into).collect())
592 }
593}
594
595impl From<&[f32]> for Value {
596 fn from(v: &[f32]) -> Self {
597 Value::Vector(v.into())
598 }
599}
600
601impl From<Arc<[f32]>> for Value {
602 fn from(v: Arc<[f32]>) -> Self {
603 Value::Vector(v)
604 }
605}
606
607impl<T: Into<Value>> From<Option<T>> for Value {
608 fn from(opt: Option<T>) -> Self {
609 match opt {
610 Some(v) => v.into(),
611 None => Value::Null,
612 }
613 }
614}
615
616#[derive(Clone, Debug)]
628pub struct HashableValue(pub Value);
629
630#[derive(Clone, Debug)]
662#[non_exhaustive]
663pub enum OrderableValue {
664 Int64(i64),
666 Float64(OrderedFloat64),
668 String(ArcStr),
670 Bool(bool),
672 Timestamp(Timestamp),
674 Date(Date),
676 Time(Time),
678 ZonedDatetime(ZonedDatetime),
680}
681
682#[derive(Clone, Copy, Debug)]
687pub struct OrderedFloat64(pub f64);
688
689impl OrderedFloat64 {
690 #[must_use]
692 pub const fn new(f: f64) -> Self {
693 Self(f)
694 }
695
696 #[must_use]
698 pub const fn get(&self) -> f64 {
699 self.0
700 }
701}
702
703impl PartialEq for OrderedFloat64 {
704 fn eq(&self, other: &Self) -> bool {
705 match (self.0.is_nan(), other.0.is_nan()) {
707 (true, true) => true,
708 (true, false) | (false, true) => false,
709 (false, false) => self.0 == other.0,
710 }
711 }
712}
713
714impl Eq for OrderedFloat64 {}
715
716impl PartialOrd for OrderedFloat64 {
717 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
718 Some(self.cmp(other))
719 }
720}
721
722impl Ord for OrderedFloat64 {
723 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
724 match (self.0.is_nan(), other.0.is_nan()) {
726 (true, true) => std::cmp::Ordering::Equal,
727 (true, false) => std::cmp::Ordering::Greater,
728 (false, true) => std::cmp::Ordering::Less,
729 (false, false) => {
730 self.0
732 .partial_cmp(&other.0)
733 .unwrap_or(std::cmp::Ordering::Equal)
734 }
735 }
736 }
737}
738
739impl Hash for OrderedFloat64 {
740 fn hash<H: Hasher>(&self, state: &mut H) {
741 self.0.to_bits().hash(state);
742 }
743}
744
745impl From<f64> for OrderedFloat64 {
746 fn from(f: f64) -> Self {
747 Self(f)
748 }
749}
750
751impl TryFrom<&Value> for OrderableValue {
752 type Error = ();
753
754 fn try_from(value: &Value) -> Result<Self, Self::Error> {
759 match value {
760 Value::Int64(i) => Ok(Self::Int64(*i)),
761 Value::Float64(f) => Ok(Self::Float64(OrderedFloat64(*f))),
762 Value::String(s) => Ok(Self::String(s.clone())),
763 Value::Bool(b) => Ok(Self::Bool(*b)),
764 Value::Timestamp(t) => Ok(Self::Timestamp(*t)),
765 Value::Date(d) => Ok(Self::Date(*d)),
766 Value::Time(t) => Ok(Self::Time(*t)),
767 Value::ZonedDatetime(zdt) => Ok(Self::ZonedDatetime(*zdt)),
768 Value::Null
769 | Value::Bytes(_)
770 | Value::Duration(_)
771 | Value::List(_)
772 | Value::Map(_)
773 | Value::Vector(_)
774 | Value::Path { .. }
775 | Value::GCounter(_)
776 | Value::OnCounter { .. } => Err(()),
777 }
778 }
779}
780
781impl OrderableValue {
782 #[must_use]
784 pub fn into_value(self) -> Value {
785 match self {
786 Self::Int64(i) => Value::Int64(i),
787 Self::Float64(f) => Value::Float64(f.0),
788 Self::String(s) => Value::String(s),
789 Self::Bool(b) => Value::Bool(b),
790 Self::Timestamp(t) => Value::Timestamp(t),
791 Self::Date(d) => Value::Date(d),
792 Self::Time(t) => Value::Time(t),
793 Self::ZonedDatetime(zdt) => Value::ZonedDatetime(zdt),
794 }
795 }
796
797 #[must_use]
799 pub const fn as_i64(&self) -> Option<i64> {
800 match self {
801 Self::Int64(i) => Some(*i),
802 _ => None,
803 }
804 }
805
806 #[must_use]
808 pub const fn as_f64(&self) -> Option<f64> {
809 match self {
810 Self::Float64(f) => Some(f.0),
811 _ => None,
812 }
813 }
814
815 #[must_use]
817 pub fn as_str(&self) -> Option<&str> {
818 match self {
819 Self::String(s) => Some(s),
820 _ => None,
821 }
822 }
823}
824
825impl PartialEq for OrderableValue {
826 fn eq(&self, other: &Self) -> bool {
827 match (self, other) {
828 (Self::Int64(a), Self::Int64(b)) => a == b,
829 (Self::Float64(a), Self::Float64(b)) => a == b,
830 (Self::String(a), Self::String(b)) => a == b,
831 (Self::Bool(a), Self::Bool(b)) => a == b,
832 (Self::Timestamp(a), Self::Timestamp(b)) => a == b,
833 (Self::Date(a), Self::Date(b)) => a == b,
834 (Self::Time(a), Self::Time(b)) => a == b,
835 (Self::ZonedDatetime(a), Self::ZonedDatetime(b)) => a == b,
836 (Self::Int64(a), Self::Float64(b)) => (*a as f64) == b.0,
838 (Self::Float64(a), Self::Int64(b)) => a.0 == (*b as f64),
839 _ => false,
840 }
841 }
842}
843
844impl Eq for OrderableValue {}
845
846impl PartialOrd for OrderableValue {
847 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
848 Some(self.cmp(other))
849 }
850}
851
852impl Ord for OrderableValue {
853 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
854 match (self, other) {
855 (Self::Int64(a), Self::Int64(b)) => a.cmp(b),
856 (Self::Float64(a), Self::Float64(b)) => a.cmp(b),
857 (Self::String(a), Self::String(b)) => a.cmp(b),
858 (Self::Bool(a), Self::Bool(b)) => a.cmp(b),
859 (Self::Timestamp(a), Self::Timestamp(b)) => a.cmp(b),
860 (Self::Date(a), Self::Date(b)) => a.cmp(b),
861 (Self::Time(a), Self::Time(b)) => a.cmp(b),
862 (Self::ZonedDatetime(a), Self::ZonedDatetime(b)) => a.cmp(b),
863 (Self::Int64(a), Self::Float64(b)) => OrderedFloat64(*a as f64).cmp(b),
865 (Self::Float64(a), Self::Int64(b)) => a.cmp(&OrderedFloat64(*b as f64)),
866 _ => self.type_ordinal().cmp(&other.type_ordinal()),
869 }
870 }
871}
872
873impl OrderableValue {
874 const fn type_ordinal(&self) -> u8 {
876 match self {
877 Self::Bool(_) => 0,
878 Self::Int64(_) => 1,
879 Self::Float64(_) => 2,
880 Self::String(_) => 3,
881 Self::Timestamp(_) => 4,
882 Self::Date(_) => 5,
883 Self::Time(_) => 6,
884 Self::ZonedDatetime(_) => 7,
885 }
886 }
887}
888
889impl Hash for OrderableValue {
890 fn hash<H: Hasher>(&self, state: &mut H) {
891 std::mem::discriminant(self).hash(state);
892 match self {
893 Self::Int64(i) => i.hash(state),
894 Self::Float64(f) => f.hash(state),
895 Self::String(s) => s.hash(state),
896 Self::Bool(b) => b.hash(state),
897 Self::Timestamp(t) => t.hash(state),
898 Self::Date(d) => d.hash(state),
899 Self::Time(t) => t.hash(state),
900 Self::ZonedDatetime(zdt) => zdt.hash(state),
901 }
902 }
903}
904
905impl HashableValue {
906 #[must_use]
908 pub fn new(value: Value) -> Self {
909 Self(value)
910 }
911
912 #[must_use]
914 pub fn inner(&self) -> &Value {
915 &self.0
916 }
917
918 #[must_use]
920 pub fn into_inner(self) -> Value {
921 self.0
922 }
923}
924
925fn hash_value<H: Hasher>(value: &Value, state: &mut H) {
927 std::mem::discriminant(value).hash(state);
928
929 match value {
930 Value::Null => {}
931 Value::Bool(b) => b.hash(state),
932 Value::Int64(i) => i.hash(state),
933 Value::Float64(f) => f.to_bits().hash(state),
934 Value::String(s) => s.hash(state),
935 Value::Bytes(b) => b.hash(state),
936 Value::Timestamp(t) => t.hash(state),
937 Value::Date(d) => d.hash(state),
938 Value::Time(t) => t.hash(state),
939 Value::Duration(d) => d.hash(state),
940 Value::ZonedDatetime(zdt) => zdt.hash(state),
941 Value::List(l) => {
942 l.len().hash(state);
943 for v in l.iter() {
944 hash_value(v, state);
945 }
946 }
947 Value::Map(m) => {
948 m.len().hash(state);
949 for (k, v) in m.iter() {
950 k.hash(state);
951 hash_value(v, state);
952 }
953 }
954 Value::Vector(v) => {
955 v.len().hash(state);
956 for &f in v.iter() {
957 f.to_bits().hash(state);
958 }
959 }
960 Value::Path { nodes, edges } => {
961 nodes.len().hash(state);
962 for v in nodes.iter() {
963 hash_value(v, state);
964 }
965 edges.len().hash(state);
966 for v in edges.iter() {
967 hash_value(v, state);
968 }
969 }
970 Value::GCounter(counts) => {
971 let mut pairs: Vec<_> = counts.iter().collect();
973 pairs.sort_by_key(|(k, _)| k.as_str());
974 pairs.len().hash(state);
975 for (k, v) in pairs {
976 k.hash(state);
977 v.hash(state);
978 }
979 }
980 Value::OnCounter { pos, neg } => {
981 let mut pos_pairs: Vec<_> = pos.iter().collect();
982 pos_pairs.sort_by_key(|(k, _)| k.as_str());
983 pos_pairs.len().hash(state);
984 for (k, v) in pos_pairs {
985 k.hash(state);
986 v.hash(state);
987 }
988 let mut neg_pairs: Vec<_> = neg.iter().collect();
989 neg_pairs.sort_by_key(|(k, _)| k.as_str());
990 neg_pairs.len().hash(state);
991 for (k, v) in neg_pairs {
992 k.hash(state);
993 v.hash(state);
994 }
995 }
996 }
997}
998
999impl Hash for HashableValue {
1000 fn hash<H: Hasher>(&self, state: &mut H) {
1001 hash_value(&self.0, state);
1002 }
1003}
1004
1005fn values_hash_eq(a: &Value, b: &Value) -> bool {
1007 match (a, b) {
1008 (Value::Float64(a), Value::Float64(b)) => a.to_bits() == b.to_bits(),
1009 (Value::List(a), Value::List(b)) => {
1010 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_hash_eq(x, y))
1011 }
1012 (Value::Map(a), Value::Map(b)) => {
1013 a.len() == b.len()
1014 && a.iter()
1015 .all(|(k, v)| b.get(k).is_some_and(|bv| values_hash_eq(v, bv)))
1016 }
1017 (Value::Vector(a), Value::Vector(b)) => {
1018 a.len() == b.len()
1019 && a.iter()
1020 .zip(b.iter())
1021 .all(|(x, y)| x.to_bits() == y.to_bits())
1022 }
1023 (
1024 Value::Path {
1025 nodes: an,
1026 edges: ae,
1027 },
1028 Value::Path {
1029 nodes: bn,
1030 edges: be,
1031 },
1032 ) => {
1033 an.len() == bn.len()
1034 && ae.len() == be.len()
1035 && an.iter().zip(bn.iter()).all(|(x, y)| values_hash_eq(x, y))
1036 && ae.iter().zip(be.iter()).all(|(x, y)| values_hash_eq(x, y))
1037 }
1038 _ => a == b,
1039 }
1040}
1041
1042impl PartialEq for HashableValue {
1043 fn eq(&self, other: &Self) -> bool {
1044 values_hash_eq(&self.0, &other.0)
1045 }
1046}
1047
1048impl Eq for HashableValue {}
1049
1050impl From<Value> for HashableValue {
1051 fn from(value: Value) -> Self {
1052 Self(value)
1053 }
1054}
1055
1056impl From<HashableValue> for Value {
1057 fn from(hv: HashableValue) -> Self {
1058 hv.0
1059 }
1060}
1061
1062#[cfg(test)]
1063mod tests {
1064 use super::*;
1065
1066 #[test]
1067 fn test_value_type_checks() {
1068 assert!(Value::Null.is_null());
1069 assert!(!Value::Bool(true).is_null());
1070
1071 assert_eq!(Value::Bool(true).as_bool(), Some(true));
1072 assert_eq!(Value::Bool(false).as_bool(), Some(false));
1073 assert_eq!(Value::Int64(42).as_bool(), None);
1074
1075 assert_eq!(Value::Int64(42).as_int64(), Some(42));
1076 assert_eq!(Value::String("test".into()).as_int64(), None);
1077
1078 assert_eq!(Value::Float64(1.234).as_float64(), Some(1.234));
1079 assert_eq!(Value::String("hello".into()).as_str(), Some("hello"));
1080 }
1081
1082 #[test]
1083 fn test_value_from_conversions() {
1084 let v: Value = true.into();
1085 assert_eq!(v.as_bool(), Some(true));
1086
1087 let v: Value = 42i64.into();
1088 assert_eq!(v.as_int64(), Some(42));
1089
1090 let v: Value = 1.234f64.into();
1091 assert_eq!(v.as_float64(), Some(1.234));
1092
1093 let v: Value = "hello".into();
1094 assert_eq!(v.as_str(), Some("hello"));
1095
1096 let v: Value = vec![1u8, 2, 3].into();
1097 assert_eq!(v.as_bytes(), Some(&[1u8, 2, 3][..]));
1098 }
1099
1100 #[test]
1101 fn test_value_serialization_roundtrip() {
1102 let values = vec![
1103 Value::Null,
1104 Value::Bool(true),
1105 Value::Int64(i64::MAX),
1106 Value::Int64(i64::MIN),
1107 Value::Int64(0),
1108 Value::Float64(std::f64::consts::PI),
1109 Value::String("hello world".into()),
1110 Value::Bytes(vec![0, 1, 2, 255].into()),
1111 Value::List(vec![Value::Int64(1), Value::Int64(2)].into()),
1112 ];
1113
1114 for v in values {
1115 let bytes = v.serialize().unwrap();
1116 let decoded = Value::deserialize(&bytes).unwrap();
1117 assert_eq!(v, decoded);
1118 }
1119 }
1120
1121 #[test]
1122 fn test_property_key() {
1123 let key = PropertyKey::new("name");
1124 assert_eq!(key.as_str(), "name");
1125
1126 let key2: PropertyKey = "age".into();
1127 assert_eq!(key2.as_str(), "age");
1128
1129 assert!(key2 < key);
1131 }
1132
1133 #[test]
1134 fn test_value_type_name() {
1135 assert_eq!(Value::Null.type_name(), "NULL");
1136 assert_eq!(Value::Bool(true).type_name(), "BOOL");
1137 assert_eq!(Value::Int64(0).type_name(), "INT64");
1138 assert_eq!(Value::Float64(0.0).type_name(), "FLOAT64");
1139 assert_eq!(Value::String("".into()).type_name(), "STRING");
1140 assert_eq!(Value::Bytes(vec![].into()).type_name(), "BYTES");
1141 assert_eq!(
1142 Value::Date(Date::from_ymd(2024, 1, 15).unwrap()).type_name(),
1143 "DATE"
1144 );
1145 assert_eq!(
1146 Value::Time(Time::from_hms(12, 0, 0).unwrap()).type_name(),
1147 "TIME"
1148 );
1149 assert_eq!(Value::Duration(Duration::default()).type_name(), "DURATION");
1150 assert_eq!(Value::List(vec![].into()).type_name(), "LIST");
1151 assert_eq!(Value::Map(BTreeMap::new().into()).type_name(), "MAP");
1152 assert_eq!(Value::Vector(vec![].into()).type_name(), "VECTOR");
1153 }
1154
1155 #[test]
1156 fn test_value_vector() {
1157 let v = Value::Vector(vec![0.1f32, 0.2, 0.3].into());
1159 assert!(v.is_vector());
1160 assert_eq!(v.vector_dimensions(), Some(3));
1161 assert_eq!(v.as_vector(), Some(&[0.1f32, 0.2, 0.3][..]));
1162
1163 let slice: &[f32] = &[1.0, 2.0, 3.0, 4.0];
1165 let v2: Value = slice.into();
1166 assert!(v2.is_vector());
1167 assert_eq!(v2.vector_dimensions(), Some(4));
1168
1169 let arc: Arc<[f32]> = vec![5.0f32, 6.0].into();
1171 let v3: Value = arc.into();
1172 assert!(v3.is_vector());
1173 assert_eq!(v3.vector_dimensions(), Some(2));
1174
1175 assert!(!Value::Int64(42).is_vector());
1177 assert_eq!(Value::Int64(42).as_vector(), None);
1178 assert_eq!(Value::Int64(42).vector_dimensions(), None);
1179 }
1180
1181 #[test]
1182 fn test_hashable_value_vector() {
1183 use std::collections::HashMap;
1184
1185 let mut map: HashMap<HashableValue, i32> = HashMap::new();
1186
1187 let v1 = HashableValue::new(Value::Vector(vec![0.1f32, 0.2, 0.3].into()));
1188 let v2 = HashableValue::new(Value::Vector(vec![0.1f32, 0.2, 0.3].into()));
1189 let v3 = HashableValue::new(Value::Vector(vec![0.4f32, 0.5, 0.6].into()));
1190
1191 map.insert(v1.clone(), 1);
1192
1193 assert_eq!(map.get(&v2), Some(&1));
1195
1196 assert_eq!(map.get(&v3), None);
1198
1199 assert_eq!(v1, v2);
1201 assert_ne!(v1, v3);
1202 }
1203
1204 #[test]
1205 fn test_orderable_value_vector_unsupported() {
1206 let v = Value::Vector(vec![0.1f32, 0.2, 0.3].into());
1208 assert!(OrderableValue::try_from(&v).is_err());
1209 }
1210
1211 #[test]
1212 fn test_hashable_value_basic() {
1213 use std::collections::HashMap;
1214
1215 let mut map: HashMap<HashableValue, i32> = HashMap::new();
1216
1217 map.insert(HashableValue::new(Value::Int64(42)), 1);
1219 map.insert(HashableValue::new(Value::String("test".into())), 2);
1220 map.insert(HashableValue::new(Value::Bool(true)), 3);
1221 map.insert(HashableValue::new(Value::Float64(std::f64::consts::PI)), 4);
1222
1223 assert_eq!(map.get(&HashableValue::new(Value::Int64(42))), Some(&1));
1224 assert_eq!(
1225 map.get(&HashableValue::new(Value::String("test".into()))),
1226 Some(&2)
1227 );
1228 assert_eq!(map.get(&HashableValue::new(Value::Bool(true))), Some(&3));
1229 assert_eq!(
1230 map.get(&HashableValue::new(Value::Float64(std::f64::consts::PI))),
1231 Some(&4)
1232 );
1233 }
1234
1235 #[test]
1236 fn test_hashable_value_float_edge_cases() {
1237 use std::collections::HashMap;
1238
1239 let mut map: HashMap<HashableValue, i32> = HashMap::new();
1240
1241 let nan = f64::NAN;
1243 map.insert(HashableValue::new(Value::Float64(nan)), 1);
1244 assert_eq!(map.get(&HashableValue::new(Value::Float64(nan))), Some(&1));
1245
1246 let pos_zero = 0.0f64;
1248 let neg_zero = -0.0f64;
1249 map.insert(HashableValue::new(Value::Float64(pos_zero)), 2);
1250 map.insert(HashableValue::new(Value::Float64(neg_zero)), 3);
1251 assert_eq!(
1252 map.get(&HashableValue::new(Value::Float64(pos_zero))),
1253 Some(&2)
1254 );
1255 assert_eq!(
1256 map.get(&HashableValue::new(Value::Float64(neg_zero))),
1257 Some(&3)
1258 );
1259 }
1260
1261 #[test]
1262 fn test_hashable_value_equality() {
1263 let v1 = HashableValue::new(Value::Int64(42));
1264 let v2 = HashableValue::new(Value::Int64(42));
1265 let v3 = HashableValue::new(Value::Int64(43));
1266
1267 assert_eq!(v1, v2);
1268 assert_ne!(v1, v3);
1269 }
1270
1271 #[test]
1272 fn test_hashable_value_inner() {
1273 let hv = HashableValue::new(Value::String("hello".into()));
1274 assert_eq!(hv.inner().as_str(), Some("hello"));
1275
1276 let v = hv.into_inner();
1277 assert_eq!(v.as_str(), Some("hello"));
1278 }
1279
1280 #[test]
1281 fn test_hashable_value_conversions() {
1282 let v = Value::Int64(42);
1283 let hv: HashableValue = v.clone().into();
1284 let v2: Value = hv.into();
1285 assert_eq!(v, v2);
1286 }
1287
1288 #[test]
1289 fn test_orderable_value_try_from() {
1290 assert!(OrderableValue::try_from(&Value::Int64(42)).is_ok());
1292 assert!(OrderableValue::try_from(&Value::Float64(std::f64::consts::PI)).is_ok());
1293 assert!(OrderableValue::try_from(&Value::String("test".into())).is_ok());
1294 assert!(OrderableValue::try_from(&Value::Bool(true)).is_ok());
1295 assert!(OrderableValue::try_from(&Value::Timestamp(Timestamp::from_secs(1000))).is_ok());
1296 assert!(
1297 OrderableValue::try_from(&Value::Date(Date::from_ymd(2024, 1, 15).unwrap())).is_ok()
1298 );
1299 assert!(OrderableValue::try_from(&Value::Time(Time::from_hms(12, 0, 0).unwrap())).is_ok());
1300
1301 assert!(OrderableValue::try_from(&Value::Null).is_err());
1303 assert!(OrderableValue::try_from(&Value::Bytes(vec![1, 2, 3].into())).is_err());
1304 assert!(OrderableValue::try_from(&Value::Duration(Duration::default())).is_err());
1305 assert!(OrderableValue::try_from(&Value::List(vec![].into())).is_err());
1306 assert!(OrderableValue::try_from(&Value::Map(BTreeMap::new().into())).is_err());
1307 }
1308
1309 #[test]
1310 fn test_orderable_value_ordering() {
1311 use std::collections::BTreeSet;
1312
1313 let mut set = BTreeSet::new();
1315 set.insert(OrderableValue::try_from(&Value::Int64(30)).unwrap());
1316 set.insert(OrderableValue::try_from(&Value::Int64(10)).unwrap());
1317 set.insert(OrderableValue::try_from(&Value::Int64(20)).unwrap());
1318 set.insert(OrderableValue::try_from(&Value::Int64(i64::MIN)).unwrap());
1319 set.insert(OrderableValue::try_from(&Value::Int64(i64::MAX)).unwrap());
1320
1321 let values: Vec<_> = set.iter().filter_map(|v| v.as_i64()).collect();
1322 assert_eq!(values, vec![i64::MIN, 10, 20, 30, i64::MAX]);
1323 }
1324
1325 #[test]
1326 fn test_orderable_value_float_ordering() {
1327 let v1 = OrderableValue::try_from(&Value::Float64(1.0)).unwrap();
1328 let v2 = OrderableValue::try_from(&Value::Float64(2.0)).unwrap();
1329 let v_nan = OrderableValue::try_from(&Value::Float64(f64::NAN)).unwrap();
1330 let v_inf = OrderableValue::try_from(&Value::Float64(f64::INFINITY)).unwrap();
1331
1332 assert!(v1 < v2);
1333 assert!(v2 < v_inf);
1334 assert!(v_inf < v_nan); assert!(v_nan == v_nan); }
1337
1338 #[test]
1339 fn test_orderable_value_string_ordering() {
1340 let a = OrderableValue::try_from(&Value::String("apple".into())).unwrap();
1341 let b = OrderableValue::try_from(&Value::String("banana".into())).unwrap();
1342 let c = OrderableValue::try_from(&Value::String("cherry".into())).unwrap();
1343
1344 assert!(a < b);
1345 assert!(b < c);
1346 }
1347
1348 #[test]
1349 fn test_orderable_value_into_value() {
1350 let original = Value::Int64(42);
1351 let orderable = OrderableValue::try_from(&original).unwrap();
1352 let back = orderable.into_value();
1353 assert_eq!(original, back);
1354
1355 let original = Value::Float64(std::f64::consts::PI);
1356 let orderable = OrderableValue::try_from(&original).unwrap();
1357 let back = orderable.into_value();
1358 assert_eq!(original, back);
1359
1360 let original = Value::String("test".into());
1361 let orderable = OrderableValue::try_from(&original).unwrap();
1362 let back = orderable.into_value();
1363 assert_eq!(original, back);
1364 }
1365
1366 #[test]
1367 fn test_orderable_value_cross_type_numeric() {
1368 let i = OrderableValue::try_from(&Value::Int64(10)).unwrap();
1370 let f = OrderableValue::try_from(&Value::Float64(10.0)).unwrap();
1371
1372 assert_eq!(i, f);
1374
1375 let f2 = OrderableValue::try_from(&Value::Float64(10.5)).unwrap();
1376 assert!(i < f2);
1377 }
1378
1379 #[test]
1380 fn test_ordered_float64_nan_handling() {
1381 let nan1 = OrderedFloat64::new(f64::NAN);
1382 let nan2 = OrderedFloat64::new(f64::NAN);
1383 let inf = OrderedFloat64::new(f64::INFINITY);
1384 let neg_inf = OrderedFloat64::new(f64::NEG_INFINITY);
1385 let zero = OrderedFloat64::new(0.0);
1386
1387 assert_eq!(nan1, nan2);
1389
1390 assert!(neg_inf < zero);
1392 assert!(zero < inf);
1393 assert!(inf < nan1);
1394 }
1395
1396 #[test]
1397 fn test_value_temporal_accessors() {
1398 let date = Date::from_ymd(2024, 3, 15).unwrap();
1399 let time = Time::from_hms(14, 30, 0).unwrap();
1400 let dur = Duration::from_months(3);
1401
1402 let vd = Value::Date(date);
1403 let vt = Value::Time(time);
1404 let vr = Value::Duration(dur);
1405
1406 assert_eq!(vd.as_date(), Some(date));
1407 assert_eq!(vt.as_time(), Some(time));
1408 assert_eq!(vr.as_duration(), Some(dur));
1409
1410 assert_eq!(vd.as_time(), None);
1412 assert_eq!(vt.as_date(), None);
1413 assert_eq!(vd.as_duration(), None);
1414 }
1415
1416 #[test]
1417 fn test_value_temporal_from_conversions() {
1418 let date = Date::from_ymd(2024, 1, 15).unwrap();
1419 let v: Value = date.into();
1420 assert_eq!(v.as_date(), Some(date));
1421
1422 let time = Time::from_hms(10, 30, 0).unwrap();
1423 let v: Value = time.into();
1424 assert_eq!(v.as_time(), Some(time));
1425
1426 let dur = Duration::from_days(7);
1427 let v: Value = dur.into();
1428 assert_eq!(v.as_duration(), Some(dur));
1429 }
1430
1431 #[test]
1432 fn test_value_temporal_display() {
1433 let v = Value::Date(Date::from_ymd(2024, 3, 15).unwrap());
1434 assert_eq!(format!("{v}"), "2024-03-15");
1435
1436 let v = Value::Time(Time::from_hms(14, 30, 0).unwrap());
1437 assert_eq!(format!("{v}"), "14:30:00");
1438
1439 let v = Value::Duration(Duration::from_days(7));
1440 assert_eq!(format!("{v}"), "P7D");
1441 }
1442
1443 #[test]
1444 fn test_value_temporal_serialization_roundtrip() {
1445 let values = vec![
1446 Value::Date(Date::from_ymd(2024, 6, 15).unwrap()),
1447 Value::Time(Time::from_hms(23, 59, 59).unwrap()),
1448 Value::Duration(Duration::new(1, 2, 3_000_000_000)),
1449 ];
1450
1451 for v in values {
1452 let bytes = v.serialize().unwrap();
1453 let decoded = Value::deserialize(&bytes).unwrap();
1454 assert_eq!(v, decoded);
1455 }
1456 }
1457
1458 #[test]
1459 fn test_orderable_value_date_ordering() {
1460 let d1 =
1461 OrderableValue::try_from(&Value::Date(Date::from_ymd(2024, 1, 1).unwrap())).unwrap();
1462 let d2 =
1463 OrderableValue::try_from(&Value::Date(Date::from_ymd(2024, 6, 15).unwrap())).unwrap();
1464 assert!(d1 < d2);
1465
1466 let back = d1.into_value();
1467 assert_eq!(back.as_date(), Some(Date::from_ymd(2024, 1, 1).unwrap()));
1468 }
1469
1470 #[test]
1471 fn test_hashable_value_temporal() {
1472 use std::collections::HashMap;
1473
1474 let mut map: HashMap<HashableValue, i32> = HashMap::new();
1475
1476 let date_val = Value::Date(Date::from_ymd(2024, 3, 15).unwrap());
1477 map.insert(HashableValue::new(date_val.clone()), 1);
1478 assert_eq!(map.get(&HashableValue::new(date_val)), Some(&1));
1479
1480 let time_val = Value::Time(Time::from_hms(12, 0, 0).unwrap());
1481 map.insert(HashableValue::new(time_val.clone()), 2);
1482 assert_eq!(map.get(&HashableValue::new(time_val)), Some(&2));
1483
1484 let dur_val = Value::Duration(Duration::from_months(6));
1485 map.insert(HashableValue::new(dur_val.clone()), 3);
1486 assert_eq!(map.get(&HashableValue::new(dur_val)), Some(&3));
1487 }
1488
1489 #[test]
1492 fn test_value_path_construction_and_equality() {
1493 let path1 = Value::Path {
1494 nodes: vec![Value::Int64(1), Value::Int64(2), Value::Int64(3)].into(),
1495 edges: vec![Value::String("KNOWS".into()), Value::String("LIKES".into())].into(),
1496 };
1497 let path2 = Value::Path {
1498 nodes: vec![Value::Int64(1), Value::Int64(2), Value::Int64(3)].into(),
1499 edges: vec![Value::String("KNOWS".into()), Value::String("LIKES".into())].into(),
1500 };
1501 let path3 = Value::Path {
1502 nodes: vec![Value::Int64(1), Value::Int64(2)].into(),
1503 edges: vec![Value::String("KNOWS".into())].into(),
1504 };
1505
1506 assert_eq!(path1, path2, "Identical paths should be equal");
1507 assert_ne!(path1, path3, "Different paths should not be equal");
1508 }
1509
1510 #[test]
1511 fn test_value_path_type_name() {
1512 let path = Value::Path {
1513 nodes: vec![Value::Int64(1)].into(),
1514 edges: vec![].into(),
1515 };
1516 assert_eq!(path.type_name(), "PATH");
1517 }
1518
1519 #[test]
1520 fn test_value_path_serialization_roundtrip() {
1521 let path = Value::Path {
1522 nodes: vec![
1523 Value::String("node_a".into()),
1524 Value::String("node_b".into()),
1525 ]
1526 .into(),
1527 edges: vec![Value::String("CONNECTS".into())].into(),
1528 };
1529
1530 let bytes = path.serialize().unwrap();
1531 let decoded = Value::deserialize(&bytes).unwrap();
1532 assert_eq!(path, decoded);
1533 }
1534
1535 #[test]
1536 fn test_value_path_hashing() {
1537 use std::collections::HashMap;
1538
1539 let path1 = Value::Path {
1540 nodes: vec![Value::Int64(1), Value::Int64(2)].into(),
1541 edges: vec![Value::String("KNOWS".into())].into(),
1542 };
1543 let path2 = Value::Path {
1544 nodes: vec![Value::Int64(1), Value::Int64(2)].into(),
1545 edges: vec![Value::String("KNOWS".into())].into(),
1546 };
1547
1548 let mut map: HashMap<HashableValue, i32> = HashMap::new();
1549 map.insert(HashableValue::new(path1), 42);
1550 assert_eq!(map.get(&HashableValue::new(path2)), Some(&42));
1551 }
1552
1553 #[test]
1556 fn test_nested_list_serialization_roundtrip() {
1557 let nested = Value::List(
1558 vec![
1559 Value::List(vec![Value::Int64(1), Value::Int64(2)].into()),
1560 Value::List(vec![Value::Int64(3), Value::Int64(4)].into()),
1561 ]
1562 .into(),
1563 );
1564
1565 let bytes = nested.serialize().unwrap();
1566 let decoded = Value::deserialize(&bytes).unwrap();
1567 assert_eq!(nested, decoded);
1568 }
1569
1570 #[test]
1571 fn test_map_with_entries_serialization_roundtrip() {
1572 let mut entries = BTreeMap::new();
1573 entries.insert(PropertyKey::new("name"), Value::String("Alix".into()));
1574 entries.insert(PropertyKey::new("age"), Value::Int64(30));
1575 entries.insert(PropertyKey::new("active"), Value::Bool(true));
1576
1577 let map = Value::Map(entries.into());
1578
1579 let bytes = map.serialize().unwrap();
1580 let decoded = Value::deserialize(&bytes).unwrap();
1581 assert_eq!(map, decoded);
1582 }
1583
1584 #[test]
1585 fn test_mixed_type_list_serialization_roundtrip() {
1586 let mixed = Value::List(
1587 vec![
1588 Value::Int64(1),
1589 Value::String("hello".into()),
1590 Value::Null,
1591 Value::Bool(false),
1592 Value::Float64(3.125),
1593 ]
1594 .into(),
1595 );
1596
1597 let bytes = mixed.serialize().unwrap();
1598 let decoded = Value::deserialize(&bytes).unwrap();
1599 assert_eq!(mixed, decoded);
1600 }
1601
1602 #[test]
1603 fn test_map_with_nested_list() {
1604 let mut entries = BTreeMap::new();
1605 entries.insert(
1606 PropertyKey::new("tags"),
1607 Value::List(vec![Value::String("a".into()), Value::String("b".into())].into()),
1608 );
1609 entries.insert(PropertyKey::new("count"), Value::Int64(2));
1610
1611 let map = Value::Map(entries.into());
1612
1613 let bytes = map.serialize().unwrap();
1614 let decoded = Value::deserialize(&bytes).unwrap();
1615 assert_eq!(map, decoded);
1616 }
1617
1618 #[test]
1619 fn test_list_with_nested_map() {
1620 let mut inner_map = BTreeMap::new();
1621 inner_map.insert(PropertyKey::new("key"), Value::String("val".into()));
1622
1623 let list = Value::List(vec![Value::Map(inner_map.into()), Value::Int64(42)].into());
1624
1625 let bytes = list.serialize().unwrap();
1626 let decoded = Value::deserialize(&bytes).unwrap();
1627 assert_eq!(list, decoded);
1628 }
1629
1630 #[test]
1631 fn test_property_key_borrow_str() {
1632 use std::collections::HashMap;
1633 let mut map: HashMap<PropertyKey, i32> = HashMap::new();
1634 map.insert(PropertyKey::new("name"), 42);
1635 map.insert(PropertyKey::new("age"), 30);
1636 assert_eq!(map.get("name"), Some(&42));
1637 assert_eq!(map.get("age"), Some(&30));
1638 assert_eq!(map.get("missing"), None);
1639 assert!(map.contains_key("name"));
1640 assert!(!map.contains_key("nope"));
1641 }
1642
1643 #[test]
1644 fn test_gcounter_type_name() {
1645 let v = Value::GCounter(Arc::new(std::collections::HashMap::new()));
1646 assert_eq!(v.type_name(), "GCOUNTER");
1647 }
1648
1649 #[test]
1650 fn test_oncounter_type_name() {
1651 let v = Value::OnCounter {
1652 pos: Arc::new(std::collections::HashMap::new()),
1653 neg: Arc::new(std::collections::HashMap::new()),
1654 };
1655 assert_eq!(v.type_name(), "PNCOUNTER");
1656 }
1657
1658 #[test]
1659 fn test_gcounter_display() {
1660 let mut counts = std::collections::HashMap::new();
1661 counts.insert("node-a".to_string(), 10u64);
1662 counts.insert("node-b".to_string(), 5u64);
1663 let v = Value::GCounter(Arc::new(counts));
1664 assert_eq!(format!("{v}"), "GCounter(15)");
1665 }
1666
1667 #[test]
1668 fn test_gcounter_debug() {
1669 let mut counts = std::collections::HashMap::new();
1670 counts.insert("node-a".to_string(), 3u64);
1671 let v = Value::GCounter(Arc::new(counts));
1672 let debug = format!("{v:?}");
1673 assert!(debug.contains("GCounter"));
1674 assert!(debug.contains("total=3"));
1675 }
1676
1677 #[test]
1678 fn test_oncounter_display() {
1679 let mut pos = std::collections::HashMap::new();
1680 pos.insert("node-a".to_string(), 10u64);
1681 pos.insert("node-b".to_string(), 3u64);
1682 let mut neg = std::collections::HashMap::new();
1683 neg.insert("node-a".to_string(), 4u64);
1684 let v = Value::OnCounter {
1685 pos: Arc::new(pos),
1686 neg: Arc::new(neg),
1687 };
1688 assert_eq!(format!("{v}"), "OnCounter(9)");
1690 }
1691
1692 #[test]
1693 fn test_oncounter_debug() {
1694 let v = Value::OnCounter {
1695 pos: Arc::new(std::collections::HashMap::new()),
1696 neg: Arc::new(std::collections::HashMap::new()),
1697 };
1698 let debug = format!("{v:?}");
1699 assert!(debug.contains("OnCounter"));
1700 assert!(debug.contains("net=0"));
1701 }
1702
1703 #[test]
1704 fn test_gcounter_hash_is_insertion_order_independent() {
1705 use std::hash::{Hash, Hasher};
1706 let mut counts1 = std::collections::HashMap::new();
1707 counts1.insert("b".to_string(), 2u64);
1708 counts1.insert("a".to_string(), 1u64);
1709 let mut counts2 = std::collections::HashMap::new();
1710 counts2.insert("a".to_string(), 1u64);
1711 counts2.insert("b".to_string(), 2u64);
1712 let v1 = HashableValue(Value::GCounter(Arc::new(counts1)));
1713 let v2 = HashableValue(Value::GCounter(Arc::new(counts2)));
1714 let mut h1 = std::collections::hash_map::DefaultHasher::new();
1715 let mut h2 = std::collections::hash_map::DefaultHasher::new();
1716 v1.hash(&mut h1);
1717 v2.hash(&mut h2);
1718 assert_eq!(h1.finish(), h2.finish());
1719 }
1720
1721 #[test]
1722 fn test_oncounter_hash_is_insertion_order_independent() {
1723 use std::hash::{Hash, Hasher};
1724 let mut pos1 = std::collections::HashMap::new();
1725 pos1.insert("b".to_string(), 5u64);
1726 pos1.insert("a".to_string(), 3u64);
1727 let mut pos2 = std::collections::HashMap::new();
1728 pos2.insert("a".to_string(), 3u64);
1729 pos2.insert("b".to_string(), 5u64);
1730 let neg = Arc::new(std::collections::HashMap::new());
1731 let v1 = HashableValue(Value::OnCounter {
1732 pos: Arc::new(pos1),
1733 neg: neg.clone(),
1734 });
1735 let v2 = HashableValue(Value::OnCounter {
1736 pos: Arc::new(pos2),
1737 neg: neg.clone(),
1738 });
1739 let mut h1 = std::collections::hash_map::DefaultHasher::new();
1740 let mut h2 = std::collections::hash_map::DefaultHasher::new();
1741 v1.hash(&mut h1);
1742 v2.hash(&mut h2);
1743 assert_eq!(h1.finish(), h2.finish());
1744 }
1745
1746 #[test]
1747 fn test_gcounter_serialize_roundtrip() {
1748 let mut counts = std::collections::HashMap::new();
1749 counts.insert("replica-1".to_string(), 42u64);
1750 counts.insert("replica-2".to_string(), 7u64);
1751 let v = Value::GCounter(Arc::new(counts));
1752 let bytes = v.serialize().unwrap();
1753 let decoded = Value::deserialize(&bytes).unwrap();
1754 assert_eq!(v, decoded);
1755 }
1756
1757 #[test]
1758 fn test_oncounter_serialize_roundtrip() {
1759 let mut pos = std::collections::HashMap::new();
1760 pos.insert("node-a".to_string(), 10u64);
1761 let mut neg = std::collections::HashMap::new();
1762 neg.insert("node-a".to_string(), 3u64);
1763 let v = Value::OnCounter {
1764 pos: Arc::new(pos),
1765 neg: Arc::new(neg),
1766 };
1767 let bytes = v.serialize().unwrap();
1768 let decoded = Value::deserialize(&bytes).unwrap();
1769 assert_eq!(v, decoded);
1770 }
1771
1772 #[test]
1773 fn test_gcounter_empty_display() {
1774 let v = Value::GCounter(Arc::new(std::collections::HashMap::new()));
1775 assert_eq!(format!("{v}"), "GCounter(0)");
1776 }
1777
1778 #[test]
1779 fn test_oncounter_zero_net() {
1780 let mut pos = std::collections::HashMap::new();
1781 pos.insert("r".to_string(), 5u64);
1782 let mut neg = std::collections::HashMap::new();
1783 neg.insert("r".to_string(), 5u64);
1784 let v = Value::OnCounter {
1785 pos: Arc::new(pos),
1786 neg: Arc::new(neg),
1787 };
1788 assert_eq!(format!("{v}"), "OnCounter(0)");
1789 }
1790}