Skip to main content

platform_value/
eq.rs

1use crate::Value;
2
3macro_rules! implpartialeq {
4    ($($t:ty),+ $(,)?) => {
5        $(
6            impl PartialEq<$t> for Value {
7                #[inline]
8                fn eq(&self, other: &$t) -> bool {
9                    if let Some(i) = self.as_integer::<$t>() {
10                        &i == other
11                    } else {
12                        false
13                    }
14                }
15            }
16
17            impl PartialEq<$t> for &Value {
18                #[inline]
19                fn eq(&self, other: &$t) -> bool {
20                    if let Some(i) = self.as_integer::<$t>() {
21                        &i == other
22                    } else {
23                        false
24                    }
25                }
26            }
27        )+
28    };
29}
30
31implpartialeq! {
32    u128,
33    u64,
34    u32,
35    u16,
36    u8,
37    i128,
38    i64,
39    i32,
40    i16,
41    i8,
42}
43
44impl PartialEq<String> for Value {
45    #[inline]
46    fn eq(&self, other: &String) -> bool {
47        if let Some(i) = self.as_text() {
48            i == other
49        } else {
50            false
51        }
52    }
53}
54
55impl PartialEq<String> for &Value {
56    #[inline]
57    fn eq(&self, other: &String) -> bool {
58        if let Some(i) = self.as_str() {
59            i == other
60        } else {
61            false
62        }
63    }
64}
65
66impl PartialEq<&str> for Value {
67    #[inline]
68    fn eq(&self, other: &&str) -> bool {
69        if let Some(i) = self.as_str() {
70            &i == other
71        } else {
72            false
73        }
74    }
75}
76
77impl PartialEq<&str> for &Value {
78    #[inline]
79    fn eq(&self, other: &&str) -> bool {
80        if let Some(i) = self.as_str() {
81            &i == other
82        } else {
83            false
84        }
85    }
86}
87
88impl PartialEq<f64> for Value {
89    #[inline]
90    fn eq(&self, other: &f64) -> bool {
91        if let Some(i) = self.as_float() {
92            &i == other
93        } else {
94            false
95        }
96    }
97}
98
99impl PartialEq<f64> for &Value {
100    #[inline]
101    fn eq(&self, other: &f64) -> bool {
102        if let Some(i) = self.as_float() {
103            &i == other
104        } else {
105            false
106        }
107    }
108}
109
110impl PartialEq<Vec<u8>> for Value {
111    #[inline]
112    fn eq(&self, other: &Vec<u8>) -> bool {
113        self.as_bytes_slice() == Ok(other.as_slice())
114    }
115}
116impl PartialEq<Vec<u8>> for &Value {
117    #[inline]
118    fn eq(&self, other: &Vec<u8>) -> bool {
119        self.as_bytes_slice() == Ok(other.as_slice())
120    }
121}
122
123macro_rules! impl_bytes_array_eq {
124    ($($n:expr),+ $(,)?) => {$(
125        impl PartialEq<[u8; $n]> for Value {
126            #[inline]
127            fn eq(&self, other: &[u8; $n]) -> bool {
128                self.as_bytes_slice() == Ok(other.as_slice())
129            }
130        }
131        impl PartialEq<[u8; $n]> for &Value {
132            #[inline]
133            fn eq(&self, other: &[u8; $n]) -> bool {
134                self.as_bytes_slice() == Ok(other.as_slice())
135            }
136        }
137    )+};
138}
139impl_bytes_array_eq! { 20, 32, 36 }
140
141impl Value {
142    /* -------------------------------------------------------- *
143     *  equality on underlying data                             *
144     * -------------------------------------------------------- */
145
146    /// Returns `true` when the *data* represented by the two `Value`s
147    /// is identical, even if they are stored in different but
148    /// compatible variants.
149    ///
150    /// * All “bytes-like” variants (`Bytes`, `Bytes20`, `Bytes32`,
151    ///   `Bytes36`, `Identifier`) compare equal when their byte
152    ///   sequences match.
153    /// * All integer variants (`U*`, `I*`) compare equal when they
154    ///   represent the same numeric value.
155    /// * Two `Map`s compare equal when they hold the same keys, in any
156    ///   order, each with equal underlying data; two `Array`s when they
157    ///   have the same length and equal underlying data position by
158    ///   position. Both recurse with the same rules, so a nested integer
159    ///   stored at a narrower width, or an object whose members were
160    ///   reordered by schema position, still compares equal.
161    /// * Otherwise falls back to normal `==` (`PartialEq`) behaviour.
162    ///
163    /// Shipped generations call this at every protocol version: the document
164    /// replace transition transformer v0 uses it to decide which fields a
165    /// replace changed, and `is_equal_ignoring_timestamps` v0 uses it when a
166    /// client verifies a state transition proof. A change to its result for
167    /// some pair of values changes their output everywhere; make such a change
168    /// a new versioned method instead of editing this function.
169    pub fn equal_underlying_data(&self, other: &Value) -> bool {
170        // 1) bytes-like cross-variant equality
171        if let (Ok(a), Ok(b)) = (self.as_bytes_slice(), other.as_bytes_slice()) {
172            return a == b;
173        }
174
175        // 2) integer cross-variant equality
176        if let (Some(a), Some(b)) = (self.as_i128_unified(), other.as_i128_unified()) {
177            return a == b;
178        }
179
180        // 3) containers, recursively and (for maps) regardless of member order
181        match (self, other) {
182            (Value::Map(this), Value::Map(that)) => {
183                if this.len() != that.len() {
184                    return false;
185                }
186                // One-to-one: a map is a list of pairs, so a duplicated key
187                // on one side must not be satisfied twice by a single entry
188                // on the other. Each matched entry is consumed.
189                let mut consumed = vec![false; that.len()];
190                for (key, value) in this {
191                    let matched =
192                        that.iter()
193                            .enumerate()
194                            .find(|(index, (other_key, other_value))| {
195                                !consumed[*index]
196                                    && key.equal_underlying_data(other_key)
197                                    && value.equal_underlying_data(other_value)
198                            });
199                    match matched {
200                        Some((index, _)) => consumed[index] = true,
201                        None => return false,
202                    }
203                }
204                true
205            }
206            (Value::Array(this), Value::Array(that)) => {
207                this.len() == that.len()
208                    && this
209                        .iter()
210                        .zip(that)
211                        .all(|(value, other_value)| value.equal_underlying_data(other_value))
212            }
213            // 4) default
214            _ => self == other,
215        }
216    }
217
218    /// Returns `true` when two single values hold the same data:
219    /// [`equal_underlying_data`](Self::equal_underlying_data) for values that
220    /// are not containers, with floats compared by their bits.
221    ///
222    /// * All "bytes-like" variants (`Bytes`, `Bytes20`, `Bytes32`, `Bytes36`,
223    ///   `Identifier`) compare equal when their byte sequences match, so an
224    ///   identifier equals the same 32 bytes carried as `Bytes32` or `Bytes`.
225    ///   `Text` is not bytes-like: a string never equals bytes.
226    /// * All integer variants (`U*`, `I*`) compare equal when they represent
227    ///   the same numeric value, whatever width each is carried at. A `U128`
228    ///   past `i128::MAX` equals only the same `U128`.
229    /// * When either side is a `Float`, both sides are read as an `f64` and
230    ///   compared by bits (`f64::to_bits`), not with `==`: `-0.0` is not
231    ///   `0.0`, as their document tree keys are not, and a NaN equals a NaN
232    ///   of the same bits, itself included. This is bit equality, not tree
233    ///   key equality: the tree key encoding maps a few distinct bit patterns
234    ///   (a negative NaN and a negative subnormal) to one key.
235    ///   An integer on the other side is read as the `f64` it converts to, as
236    ///   [`as_float`](Self::as_float) reads it: a document stores `date` and
237    ///   `number` properties as floats while a transition may carry one as an
238    ///   integer. Past 2^53 an integer rounds to the nearest `f64`, as it does
239    ///   when stored, so the comparison is not transitive there: `2^53 + 1`
240    ///   and `2^53` both equal the float `2^53` but not each other.
241    /// * An `Array` whose every element is a `U8` holds the bytes it lists, as
242    ///   [`to_identifier_bytes`](Self::to_identifier_bytes) reads it, so it
243    ///   equals the same bytes carried as bytes, an identifier, or another
244    ///   such array: a transition may carry an identifier or a byte array
245    ///   that way. Any other `Array`, and a `Map` on either side, is not a
246    ///   single value and never compares equal, not even to an equal
247    ///   container.
248    /// * Otherwise the two must be the same variant and compare with `==`:
249    ///   two `Null`s are equal and a `Null` equals nothing else, text compares
250    ///   as text (so `""` is not `"\0"`, though a tree key encodes both as
251    ///   `[0]`), and booleans as booleans.
252    ///
253    /// A value of any size compares: two equal strings or byte arrays longer
254    /// than the 255 bytes a tree key holds are equal.
255    ///
256    /// From protocol version 14, document reference validation 0 judges each
257    /// `propertyAgreement` pair of a `refersTo` reference with it. A change to
258    /// its result for some pair of values changes which documents are
259    /// accepted; make such a change a new method instead of editing this one.
260    pub fn same_scalar_data(&self, other: &Value) -> bool {
261        /// The bytes `value` holds: a bytes-like variant's, or those an array
262        /// of `U8`s lists. `None` for anything else.
263        fn held_bytes(value: &Value) -> Option<Vec<u8>> {
264            match value {
265                Value::Array(items) => items
266                    .iter()
267                    .map(|item| match item {
268                        Value::U8(byte) => Some(*byte),
269                        _ => None,
270                    })
271                    .collect(),
272                _ => value.as_bytes_slice().ok().map(<[u8]>::to_vec),
273            }
274        }
275
276        match (self, other) {
277            // 1) an array is a single value only as the bytes it lists
278            (Value::Array(_), _) | (_, Value::Array(_)) => matches!(
279                (held_bytes(self), held_bytes(other)),
280                (Some(this), Some(that)) if this == that
281            ),
282            // 2) a map is no single value
283            (Value::Map(_), _) | (_, Value::Map(_)) => false,
284            // 3) floats by bits, an integer read as the float it converts to
285            (Value::Float(_), _) | (_, Value::Float(_)) => matches!(
286                (self.as_float(), other.as_float()),
287                (Some(this), Some(that)) if this.to_bits() == that.to_bits()
288            ),
289            // 4) bytes by bytes, integers by value, anything else by `==`
290            _ => self.equal_underlying_data(other),
291        }
292    }
293}
294
295#[cfg(test)]
296mod underlying_data_tests {
297    use crate::Value;
298
299    fn map(entries: Vec<(&str, Value)>) -> Value {
300        Value::Map(
301            entries
302                .into_iter()
303                .map(|(key, value)| (Value::Text(key.to_string()), value))
304                .collect(),
305        )
306    }
307
308    /// The two ways storage rewrites an object a client sent: integer
309    /// widths shrink to the smallest fitting variant and members are
310    /// reordered by schema position.
311    #[test]
312    fn maps_compare_equal_across_member_order_and_integer_width() {
313        let sent = map(vec![
314            ("rank", Value::U64(7)),
315            ("tag", Value::Text("intro".into())),
316        ]);
317        let stored = map(vec![
318            ("tag", Value::Text("intro".into())),
319            ("rank", Value::U8(7)),
320        ]);
321
322        assert!(sent.equal_underlying_data(&stored));
323        assert!(stored.equal_underlying_data(&sent));
324    }
325
326    #[test]
327    fn maps_differ_on_a_changed_nested_value() {
328        let before = map(vec![
329            ("rank", Value::U64(7)),
330            ("tag", Value::Text("intro".into())),
331        ]);
332        let after = map(vec![
333            ("tag", Value::Text("intro".into())),
334            ("rank", Value::U8(8)),
335        ]);
336
337        assert!(!before.equal_underlying_data(&after));
338    }
339
340    #[test]
341    fn maps_differ_on_a_missing_or_extra_member() {
342        let one = map(vec![("tag", Value::Text("intro".into()))]);
343        let two = map(vec![
344            ("tag", Value::Text("intro".into())),
345            ("rank", Value::U8(7)),
346        ]);
347
348        assert!(!one.equal_underlying_data(&two));
349        assert!(!two.equal_underlying_data(&one));
350    }
351
352    #[test]
353    fn nested_maps_recurse() {
354        let sent = map(vec![(
355            "meta",
356            map(vec![
357                ("rank", Value::I64(7)),
358                ("tag", Value::Text("a".into())),
359            ]),
360        )]);
361        let stored = map(vec![(
362            "meta",
363            map(vec![
364                ("tag", Value::Text("a".into())),
365                ("rank", Value::U8(7)),
366            ]),
367        )]);
368
369        assert!(sent.equal_underlying_data(&stored));
370    }
371
372    #[test]
373    fn arrays_compare_position_by_position_with_integer_leniency() {
374        let sent = Value::Array(vec![Value::U64(1), Value::U64(2)]);
375        let stored = Value::Array(vec![Value::U8(1), Value::U8(2)]);
376        let reordered = Value::Array(vec![Value::U8(2), Value::U8(1)]);
377        let shorter = Value::Array(vec![Value::U8(1)]);
378
379        assert!(sent.equal_underlying_data(&stored));
380        assert!(!sent.equal_underlying_data(&reordered));
381        assert!(!sent.equal_underlying_data(&shorter));
382    }
383
384    /// A map is a list of pairs, so a key can in principle appear twice.
385    /// Two entries on one side must not both be satisfied by the single
386    /// entry they match on the other.
387    #[test]
388    fn maps_match_entries_one_to_one() {
389        let doubled = map(vec![("tag", Value::U8(1)), ("tag", Value::U8(1))]);
390        let single_plus_other = map(vec![("tag", Value::U8(1)), ("rank", Value::U8(1))]);
391        let doubled_too = map(vec![("tag", Value::U64(1)), ("tag", Value::U64(1))]);
392
393        assert!(!doubled.equal_underlying_data(&single_plus_other));
394        assert!(!single_plus_other.equal_underlying_data(&doubled));
395        assert!(doubled.equal_underlying_data(&doubled_too));
396    }
397
398    #[test]
399    fn a_map_never_equals_a_non_map() {
400        let a_map = map(vec![("tag", Value::Text("a".into()))]);
401
402        assert!(!a_map.equal_underlying_data(&Value::Text("a".into())));
403        assert!(!a_map.equal_underlying_data(&Value::Array(vec![])));
404    }
405}
406
407#[cfg(test)]
408mod same_scalar_data_tests {
409    use crate::Value;
410
411    /// Compares both ways, checking the two directions agree.
412    fn same(left: &Value, right: &Value) -> bool {
413        let forward = left.same_scalar_data(right);
414        assert_eq!(
415            forward,
416            right.same_scalar_data(left),
417            "{left:?} and {right:?} compare differently each way"
418        );
419        forward
420    }
421
422    /// A tree key encodes both as `[0]`; as values they differ.
423    #[test]
424    fn should_not_equate_the_empty_string_with_a_nul_character() {
425        let empty = Value::Text(String::new());
426        let nul = Value::Text("\0".to_string());
427
428        assert!(!same(&empty, &nul));
429        assert!(same(&empty, &empty.clone()));
430        assert!(same(&nul, &nul.clone()));
431    }
432
433    /// A transition may carry an integer at another width than the stored
434    /// document decodes it at.
435    #[test]
436    fn should_equate_the_same_integer_carried_at_different_widths() {
437        assert!(same(&Value::U64(513), &Value::U16(513)));
438        assert!(same(&Value::I64(-7), &Value::I32(-7)));
439        assert!(same(&Value::U8(100), &Value::I128(100)));
440        assert!(!same(&Value::U64(514), &Value::U16(513)));
441        assert!(!same(&Value::I8(-1), &Value::U64(255)));
442    }
443
444    #[test]
445    fn should_equate_a_u128_past_i128_only_with_the_same_u128() {
446        let past_i128 = u128::MAX;
447
448        assert!(same(&Value::U128(past_i128), &Value::U128(past_i128)));
449        assert!(!same(&Value::U128(past_i128), &Value::U128(past_i128 - 1)));
450        assert!(!same(
451            &Value::U128(i128::MAX as u128 + 1),
452            &Value::I128(i128::MAX)
453        ));
454        assert!(!same(&Value::U128(past_i128), &Value::U64(u64::MAX)));
455        assert!(same(
456            &Value::U128(i128::MAX as u128),
457            &Value::I128(i128::MAX)
458        ));
459    }
460
461    #[test]
462    fn should_equate_an_identifier_with_the_same_32_bytes() {
463        let id = [7u8; 32];
464
465        assert!(same(&Value::Identifier(id), &Value::Bytes32(id)));
466        assert!(same(&Value::Bytes(id.to_vec()), &Value::Identifier(id)));
467        assert!(!same(&Value::Identifier(id), &Value::Identifier([8u8; 32])));
468        assert!(!same(&Value::Identifier(id), &Value::Bytes(vec![7u8; 31])));
469    }
470
471    /// No tree key holds more than 255 bytes; a value of any size compares.
472    #[test]
473    fn should_equate_equal_strings_and_byte_arrays_longer_than_a_tree_key() {
474        let long_ascii = Value::Text("a".repeat(256));
475        // 280 bytes of UTF-8 in 70 characters
476        let long_emoji = Value::Text("\u{1F600}".repeat(70));
477
478        assert!(same(&long_ascii, &long_ascii.clone()));
479        assert!(same(&long_emoji, &long_emoji.clone()));
480        assert!(!same(&long_ascii, &Value::Text("a".repeat(257))));
481        assert!(same(
482            &Value::Bytes(vec![9; 300]),
483            &Value::Bytes(vec![9; 300])
484        ));
485        assert!(!same(
486            &Value::Bytes(vec![9; 300]),
487            &Value::Bytes(vec![9; 301])
488        ));
489    }
490
491    /// Floats compare by bits, where `equal_underlying_data` compares them
492    /// with `==`.
493    #[test]
494    fn should_compare_floats_by_their_bits() {
495        let quiet_nan = Value::Float(f64::NAN);
496        let other_nan = Value::Float(f64::from_bits(f64::NAN.to_bits() | 1));
497
498        assert!(!same(&Value::Float(-0.0), &Value::Float(0.0)));
499        assert!(Value::Float(-0.0).equal_underlying_data(&Value::Float(0.0)));
500        assert!(same(&Value::Float(1.5), &Value::Float(1.5)));
501        assert!(!same(&Value::Float(1.5), &Value::Float(2.5)));
502        assert!(same(&quiet_nan, &quiet_nan.clone()));
503        assert!(!quiet_nan.equal_underlying_data(&quiet_nan.clone()));
504        assert!(!same(&quiet_nan, &other_nan));
505    }
506
507    /// A document stores a date as a float; a transition may carry it as an
508    /// integer.
509    #[test]
510    fn should_read_an_integer_against_a_float_as_the_float_it_converts_to() {
511        assert!(same(&Value::U64(1_700_000_000_000), &Value::Float(1.7e12)));
512        assert!(!same(&Value::U64(1_700_000_000_001), &Value::Float(1.7e12)));
513        assert!(same(&Value::I32(-3), &Value::Float(-3.0)));
514        assert!(same(&Value::U64(0), &Value::Float(0.0)));
515        assert!(!same(&Value::U64(0), &Value::Float(-0.0)));
516        assert!(!same(&Value::Text("1".to_string()), &Value::Float(1.0)));
517        assert!(!same(&Value::Bool(true), &Value::Float(1.0)));
518    }
519
520    /// Past 2^53 an integer rounds to the nearest `f64`, as storage rounds
521    /// it, so two integers that differ can both equal one float.
522    #[test]
523    fn should_round_an_integer_past_2_pow_53_to_the_nearest_float() {
524        let two_pow_53 = 1u64 << 53;
525        let float_two_pow_53 = Value::Float(two_pow_53 as f64);
526
527        assert!(same(&Value::U64(two_pow_53 + 1), &float_two_pow_53));
528        assert!(same(&Value::U64(two_pow_53), &float_two_pow_53));
529        assert!(!same(&Value::U64(two_pow_53 + 1), &Value::U64(two_pow_53)));
530
531        assert!(same(
532            &Value::I128(i128::MIN),
533            &Value::Float(-(2.0f64.powi(127)))
534        ));
535        assert!(same(
536            &Value::U128(u128::MAX),
537            &Value::Float(2.0f64.powi(128))
538        ));
539        assert!(!same(&Value::U128(u128::MAX), &Value::Float(f64::INFINITY)));
540    }
541
542    #[test]
543    fn should_never_equate_a_map_or_an_array_of_anything_but_bytes() {
544        let empty_map = Value::Map(vec![]);
545        let one_entry_map = Value::Map(vec![(Value::Text("a".into()), Value::U8(1))]);
546        let wide_integers = Value::Array(vec![Value::U64(1), Value::U64(2)]);
547        let texts = Value::Array(vec![Value::Text("a".into())]);
548
549        assert!(!same(&empty_map, &empty_map.clone()));
550        assert!(!same(&one_entry_map, &one_entry_map.clone()));
551        assert!(one_entry_map.equal_underlying_data(&one_entry_map.clone()));
552        assert!(!same(&wide_integers, &wide_integers.clone()));
553        assert!(!same(&wide_integers, &Value::Bytes(vec![1, 2])));
554        assert!(!same(&texts, &texts.clone()));
555        assert!(!same(&empty_map, &Value::Null));
556        assert!(!same(&empty_map, &Value::Array(vec![])));
557    }
558
559    /// A transition may carry an identifier or a byte array as an array of
560    /// `U8`s, which `to_identifier_bytes` reads as those bytes.
561    #[test]
562    fn should_read_an_array_of_u8_as_the_bytes_it_lists() {
563        let id = [7u8; 32];
564        let listed = Value::Array(id.iter().copied().map(Value::U8).collect());
565
566        assert!(same(&listed, &Value::Identifier(id)));
567        assert!(same(&listed, &Value::Bytes32(id)));
568        assert!(same(&listed, &Value::Bytes(id.to_vec())));
569        assert!(same(&listed, &listed.clone()));
570        assert!(same(&Value::Array(vec![]), &Value::Bytes(vec![])));
571        assert!(!same(&listed, &Value::Identifier([8u8; 32])));
572        assert!(!same(&Value::Array(vec![Value::U8(1)]), &Value::Float(1.0)));
573        assert!(!same(&Value::Array(vec![Value::U8(1)]), &Value::U8(1)));
574        assert!(!same(
575            &Value::Array(vec![Value::U8(97)]),
576            &Value::Text("a".into())
577        ));
578        assert!(!same(&Value::Array(vec![Value::U8(1)]), &Value::Null));
579    }
580
581    #[test]
582    fn should_equate_two_nulls_and_nothing_else_with_a_null() {
583        assert!(same(&Value::Null, &Value::Null));
584        assert!(!same(&Value::Null, &Value::Text(String::new())));
585        assert!(!same(&Value::Null, &Value::Bytes(vec![])));
586        assert!(!same(&Value::Null, &Value::U64(0)));
587        assert!(!same(&Value::Null, &Value::Bool(false)));
588        assert!(!same(&Value::Null, &Value::Float(0.0)));
589    }
590
591    #[test]
592    fn should_not_equate_values_of_different_kinds() {
593        assert!(!same(&Value::Text("1".to_string()), &Value::U64(1)));
594        assert!(!same(&Value::Bool(true), &Value::U8(1)));
595        assert!(!same(&Value::U64(1), &Value::Bool(true)));
596        assert!(!same(
597            &Value::Text("abc".to_string()),
598            &Value::Bytes(b"abc".to_vec())
599        ));
600        assert!(same(&Value::Bool(true), &Value::Bool(true)));
601        assert!(!same(&Value::Bool(true), &Value::Bool(false)));
602    }
603}
604
605#[cfg(test)]
606#[allow(clippy::approx_constant)]
607mod tests {
608    use crate::Value;
609
610    // ---- PartialEq<integer types> ----
611
612    #[test]
613    fn u8_eq() {
614        assert_eq!(Value::U8(42), 42u8);
615        assert_ne!(Value::U8(42), 43u8);
616    }
617
618    #[test]
619    fn i8_eq() {
620        assert_eq!(Value::I8(-1), -1i8);
621        assert_ne!(Value::I8(-1), 0i8);
622    }
623
624    #[test]
625    fn u16_eq() {
626        assert_eq!(Value::U16(1000), 1000u16);
627        assert_ne!(Value::U16(1000), 999u16);
628    }
629
630    #[test]
631    fn i16_eq() {
632        assert_eq!(Value::I16(-500), -500i16);
633        assert_ne!(Value::I16(-500), 500i16);
634    }
635
636    #[test]
637    fn u32_eq() {
638        assert_eq!(Value::U32(100_000), 100_000u32);
639        assert_ne!(Value::U32(100_000), 0u32);
640    }
641
642    #[test]
643    fn i32_eq() {
644        assert_eq!(Value::I32(-100), -100i32);
645        assert_ne!(Value::I32(-100), 100i32);
646    }
647
648    #[test]
649    fn u64_eq() {
650        assert_eq!(Value::U64(u64::MAX), u64::MAX);
651        assert_ne!(Value::U64(0), 1u64);
652    }
653
654    #[test]
655    fn i64_eq() {
656        assert_eq!(Value::I64(i64::MIN), i64::MIN);
657        assert_ne!(Value::I64(0), 1i64);
658    }
659
660    #[test]
661    fn u128_eq() {
662        assert_eq!(Value::U128(u128::MAX), u128::MAX);
663        assert_ne!(Value::U128(0), 1u128);
664    }
665
666    #[test]
667    fn i128_eq() {
668        assert_eq!(Value::I128(i128::MIN), i128::MIN);
669        assert_ne!(Value::I128(0), 1i128);
670    }
671
672    // ---- cross-type integer comparison via as_integer ----
673
674    #[test]
675    fn u8_value_eq_u64_type() {
676        // Value::U8(10) should equal 10u64 through as_integer
677        assert_eq!(Value::U8(10), 10u64);
678    }
679
680    #[test]
681    fn u64_value_eq_u8_type_when_fits() {
682        assert_eq!(Value::U64(200), 200u8);
683    }
684
685    #[test]
686    fn u64_value_ne_u8_type_when_overflow() {
687        // 256 doesn't fit in u8
688        assert_ne!(Value::U64(256), 0u8); // as_integer::<u8> returns None
689    }
690
691    #[test]
692    fn i8_value_eq_i64_type() {
693        assert_eq!(Value::I8(-10), -10i64);
694    }
695
696    #[test]
697    fn non_integer_ne_integer() {
698        assert_ne!(Value::Text("hello".to_string()), 0u64);
699        assert_ne!(Value::Null, 0i32);
700        assert_ne!(Value::Bool(true), 1u8);
701    }
702
703    // ---- PartialEq<String> ----
704
705    #[test]
706    fn string_eq() {
707        let val = Value::Text("hello".to_string());
708        assert_eq!(val, "hello".to_string());
709        assert_ne!(val, "world".to_string());
710    }
711
712    #[test]
713    fn non_text_ne_string() {
714        assert_ne!(Value::U8(0), "0".to_string());
715        assert_ne!(Value::Null, "".to_string());
716    }
717
718    // ---- PartialEq<&str> ----
719
720    #[test]
721    fn str_ref_eq() {
722        let val = Value::Text("test".to_string());
723        assert_eq!(val, "test");
724        assert_ne!(val, "other");
725    }
726
727    #[test]
728    fn non_text_ne_str_ref() {
729        assert_ne!(Value::Bool(false), "false");
730    }
731
732    // ---- PartialEq<f64> ----
733
734    #[test]
735    fn float_eq() {
736        assert_eq!(Value::Float(3.14), 3.14f64);
737        assert_ne!(Value::Float(3.14), 3.15f64);
738    }
739
740    #[test]
741    fn integer_eq_float_through_as_float() {
742        // as_float converts integers to f64, so Value::U64(10) == 10.0f64
743        assert_eq!(Value::U64(10), 10.0f64);
744    }
745
746    #[test]
747    fn non_numeric_ne_float() {
748        assert_ne!(Value::Text("3.14".to_string()), 3.14f64);
749    }
750
751    // ---- PartialEq<Vec<u8>> ----
752
753    #[test]
754    fn bytes_eq_vec_u8() {
755        let data = vec![1, 2, 3];
756        assert_eq!(Value::Bytes(data.clone()), data);
757    }
758
759    #[test]
760    fn bytes_ne_vec_u8() {
761        assert_ne!(Value::Bytes(vec![1, 2, 3]), vec![1, 2, 4]);
762    }
763
764    #[test]
765    fn identifier_eq_vec_u8() {
766        let id = [42u8; 32];
767        assert_eq!(Value::Identifier(id), id.to_vec());
768    }
769
770    #[test]
771    fn bytes20_eq_vec_u8() {
772        let b = [5u8; 20];
773        assert_eq!(Value::Bytes20(b), b.to_vec());
774    }
775
776    #[test]
777    fn non_bytes_ne_vec_u8() {
778        assert_ne!(Value::U8(1), vec![1u8]);
779    }
780
781    // ---- PartialEq<[u8; 32]> ----
782
783    #[test]
784    fn bytes32_eq_array() {
785        let b = [0xffu8; 32];
786        assert_eq!(Value::Bytes32(b), b);
787    }
788
789    #[test]
790    fn identifier_eq_array_32() {
791        let id = [7u8; 32];
792        assert_eq!(Value::Identifier(id), id);
793    }
794
795    #[test]
796    fn bytes_eq_array_32() {
797        let data = [3u8; 32];
798        assert_eq!(Value::Bytes(data.to_vec()), data);
799    }
800
801    #[test]
802    fn non_bytes_ne_array_32() {
803        assert_ne!(Value::Null, [0u8; 32]);
804    }
805
806    // ---- PartialEq<[u8; 20]> ----
807
808    #[test]
809    fn bytes20_eq_array_20() {
810        let b = [1u8; 20];
811        assert_eq!(Value::Bytes20(b), b);
812    }
813
814    // ---- PartialEq<[u8; 36]> ----
815
816    #[test]
817    fn bytes36_eq_array_36() {
818        let b = [2u8; 36];
819        assert_eq!(Value::Bytes36(b), b);
820    }
821
822    // ---- PartialEq for &Value ----
823
824    #[test]
825    fn ref_value_eq_integer() {
826        let val = Value::U64(42);
827        assert_eq!(&val, 42u64);
828    }
829
830    #[test]
831    fn ref_value_eq_string() {
832        let val = Value::Text("hi".to_string());
833        assert_eq!(&val, "hi".to_string());
834    }
835
836    #[test]
837    fn ref_value_eq_str_ref() {
838        let val = Value::Text("hi".to_string());
839        assert_eq!(&val, "hi");
840    }
841
842    #[test]
843    fn ref_value_eq_float() {
844        let val = Value::Float(1.0);
845        assert_eq!(&val, 1.0f64);
846    }
847
848    #[test]
849    fn ref_value_eq_vec_u8() {
850        let val = Value::Bytes(vec![10, 20]);
851        assert_eq!(&val, vec![10u8, 20]);
852    }
853
854    #[test]
855    fn ref_value_eq_array_32() {
856        let b = [0u8; 32];
857        let val = Value::Bytes32(b);
858        assert_eq!(&val, b);
859    }
860
861    // ---- equal_underlying_data tests ----
862
863    #[test]
864    fn equal_underlying_data_bytes_vs_identifier_same_data() {
865        let data = [42u8; 32];
866        let bytes = Value::Bytes(data.to_vec());
867        let ident = Value::Identifier(data);
868        assert!(bytes.equal_underlying_data(&ident));
869        assert!(ident.equal_underlying_data(&bytes));
870    }
871
872    #[test]
873    fn equal_underlying_data_bytes_vs_identifier_different_data() {
874        let bytes = Value::Bytes(vec![0u8; 32]);
875        let ident = Value::Identifier([1u8; 32]);
876        assert!(!bytes.equal_underlying_data(&ident));
877    }
878
879    #[test]
880    fn equal_underlying_data_bytes32_vs_identifier() {
881        let data = [99u8; 32];
882        let b32 = Value::Bytes32(data);
883        let ident = Value::Identifier(data);
884        assert!(b32.equal_underlying_data(&ident));
885    }
886
887    #[test]
888    fn equal_underlying_data_bytes20_vs_bytes() {
889        let data = [5u8; 20];
890        let b20 = Value::Bytes20(data);
891        let bytes = Value::Bytes(data.to_vec());
892        assert!(b20.equal_underlying_data(&bytes));
893    }
894
895    #[test]
896    fn equal_underlying_data_u8_vs_u64_same_value() {
897        let a = Value::U8(10);
898        let b = Value::U64(10);
899        assert!(a.equal_underlying_data(&b));
900    }
901
902    #[test]
903    fn equal_underlying_data_i8_vs_i128_same_value() {
904        let a = Value::I8(-5);
905        let b = Value::I128(-5);
906        assert!(a.equal_underlying_data(&b));
907    }
908
909    #[test]
910    fn equal_underlying_data_u8_vs_u64_different_value() {
911        let a = Value::U8(10);
912        let b = Value::U64(20);
913        assert!(!a.equal_underlying_data(&b));
914    }
915
916    #[test]
917    fn equal_underlying_data_u16_vs_i32_same_value() {
918        let a = Value::U16(100);
919        let b = Value::I32(100);
920        assert!(a.equal_underlying_data(&b));
921    }
922
923    #[test]
924    fn equal_underlying_data_negative_i8_vs_u64() {
925        // negative can't match unsigned
926        let a = Value::I8(-1);
927        let b = Value::U64(255);
928        assert!(!a.equal_underlying_data(&b));
929    }
930
931    #[test]
932    fn equal_underlying_data_same_variant_same_value() {
933        let a = Value::U64(42);
934        let b = Value::U64(42);
935        assert!(a.equal_underlying_data(&b));
936    }
937
938    #[test]
939    fn equal_underlying_data_fallback_to_partial_eq() {
940        // Text vs Text uses default PartialEq
941        let a = Value::Text("hello".to_string());
942        let b = Value::Text("hello".to_string());
943        assert!(a.equal_underlying_data(&b));
944
945        let c = Value::Text("world".to_string());
946        assert!(!a.equal_underlying_data(&c));
947    }
948
949    #[test]
950    fn equal_underlying_data_null_vs_null() {
951        assert!(Value::Null.equal_underlying_data(&Value::Null));
952    }
953
954    #[test]
955    fn equal_underlying_data_different_types_not_equal() {
956        // A string vs a number should not be equal
957        let a = Value::Text("42".to_string());
958        let b = Value::U64(42);
959        assert!(!a.equal_underlying_data(&b));
960    }
961
962    #[test]
963    fn equal_underlying_data_bool_vs_bool() {
964        assert!(Value::Bool(true).equal_underlying_data(&Value::Bool(true)));
965        assert!(!Value::Bool(true).equal_underlying_data(&Value::Bool(false)));
966    }
967
968    #[test]
969    fn equal_underlying_data_float_vs_float() {
970        assert!(Value::Float(1.5).equal_underlying_data(&Value::Float(1.5)));
971        assert!(!Value::Float(1.5).equal_underlying_data(&Value::Float(2.5)));
972    }
973}