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