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    /// * Otherwise falls back to normal `==` (`PartialEq`) behaviour.
156    #[inline]
157    pub fn equal_underlying_data(&self, other: &Value) -> bool {
158        // 1) bytes-like cross-variant equality
159        if let (Ok(a), Ok(b)) = (self.as_bytes_slice(), other.as_bytes_slice()) {
160            return a == b;
161        }
162
163        // 2) integer cross-variant equality
164        if let (Some(a), Some(b)) = (self.as_i128_unified(), other.as_i128_unified()) {
165            return a == b;
166        }
167
168        // 3) default
169        self == other
170    }
171}
172
173#[cfg(test)]
174#[allow(clippy::approx_constant)]
175mod tests {
176    use crate::Value;
177
178    // ---- PartialEq<integer types> ----
179
180    #[test]
181    fn u8_eq() {
182        assert_eq!(Value::U8(42), 42u8);
183        assert_ne!(Value::U8(42), 43u8);
184    }
185
186    #[test]
187    fn i8_eq() {
188        assert_eq!(Value::I8(-1), -1i8);
189        assert_ne!(Value::I8(-1), 0i8);
190    }
191
192    #[test]
193    fn u16_eq() {
194        assert_eq!(Value::U16(1000), 1000u16);
195        assert_ne!(Value::U16(1000), 999u16);
196    }
197
198    #[test]
199    fn i16_eq() {
200        assert_eq!(Value::I16(-500), -500i16);
201        assert_ne!(Value::I16(-500), 500i16);
202    }
203
204    #[test]
205    fn u32_eq() {
206        assert_eq!(Value::U32(100_000), 100_000u32);
207        assert_ne!(Value::U32(100_000), 0u32);
208    }
209
210    #[test]
211    fn i32_eq() {
212        assert_eq!(Value::I32(-100), -100i32);
213        assert_ne!(Value::I32(-100), 100i32);
214    }
215
216    #[test]
217    fn u64_eq() {
218        assert_eq!(Value::U64(u64::MAX), u64::MAX);
219        assert_ne!(Value::U64(0), 1u64);
220    }
221
222    #[test]
223    fn i64_eq() {
224        assert_eq!(Value::I64(i64::MIN), i64::MIN);
225        assert_ne!(Value::I64(0), 1i64);
226    }
227
228    #[test]
229    fn u128_eq() {
230        assert_eq!(Value::U128(u128::MAX), u128::MAX);
231        assert_ne!(Value::U128(0), 1u128);
232    }
233
234    #[test]
235    fn i128_eq() {
236        assert_eq!(Value::I128(i128::MIN), i128::MIN);
237        assert_ne!(Value::I128(0), 1i128);
238    }
239
240    // ---- cross-type integer comparison via as_integer ----
241
242    #[test]
243    fn u8_value_eq_u64_type() {
244        // Value::U8(10) should equal 10u64 through as_integer
245        assert_eq!(Value::U8(10), 10u64);
246    }
247
248    #[test]
249    fn u64_value_eq_u8_type_when_fits() {
250        assert_eq!(Value::U64(200), 200u8);
251    }
252
253    #[test]
254    fn u64_value_ne_u8_type_when_overflow() {
255        // 256 doesn't fit in u8
256        assert_ne!(Value::U64(256), 0u8); // as_integer::<u8> returns None
257    }
258
259    #[test]
260    fn i8_value_eq_i64_type() {
261        assert_eq!(Value::I8(-10), -10i64);
262    }
263
264    #[test]
265    fn non_integer_ne_integer() {
266        assert_ne!(Value::Text("hello".to_string()), 0u64);
267        assert_ne!(Value::Null, 0i32);
268        assert_ne!(Value::Bool(true), 1u8);
269    }
270
271    // ---- PartialEq<String> ----
272
273    #[test]
274    fn string_eq() {
275        let val = Value::Text("hello".to_string());
276        assert_eq!(val, "hello".to_string());
277        assert_ne!(val, "world".to_string());
278    }
279
280    #[test]
281    fn non_text_ne_string() {
282        assert_ne!(Value::U8(0), "0".to_string());
283        assert_ne!(Value::Null, "".to_string());
284    }
285
286    // ---- PartialEq<&str> ----
287
288    #[test]
289    fn str_ref_eq() {
290        let val = Value::Text("test".to_string());
291        assert_eq!(val, "test");
292        assert_ne!(val, "other");
293    }
294
295    #[test]
296    fn non_text_ne_str_ref() {
297        assert_ne!(Value::Bool(false), "false");
298    }
299
300    // ---- PartialEq<f64> ----
301
302    #[test]
303    fn float_eq() {
304        assert_eq!(Value::Float(3.14), 3.14f64);
305        assert_ne!(Value::Float(3.14), 3.15f64);
306    }
307
308    #[test]
309    fn integer_eq_float_through_as_float() {
310        // as_float converts integers to f64, so Value::U64(10) == 10.0f64
311        assert_eq!(Value::U64(10), 10.0f64);
312    }
313
314    #[test]
315    fn non_numeric_ne_float() {
316        assert_ne!(Value::Text("3.14".to_string()), 3.14f64);
317    }
318
319    // ---- PartialEq<Vec<u8>> ----
320
321    #[test]
322    fn bytes_eq_vec_u8() {
323        let data = vec![1, 2, 3];
324        assert_eq!(Value::Bytes(data.clone()), data);
325    }
326
327    #[test]
328    fn bytes_ne_vec_u8() {
329        assert_ne!(Value::Bytes(vec![1, 2, 3]), vec![1, 2, 4]);
330    }
331
332    #[test]
333    fn identifier_eq_vec_u8() {
334        let id = [42u8; 32];
335        assert_eq!(Value::Identifier(id), id.to_vec());
336    }
337
338    #[test]
339    fn bytes20_eq_vec_u8() {
340        let b = [5u8; 20];
341        assert_eq!(Value::Bytes20(b), b.to_vec());
342    }
343
344    #[test]
345    fn non_bytes_ne_vec_u8() {
346        assert_ne!(Value::U8(1), vec![1u8]);
347    }
348
349    // ---- PartialEq<[u8; 32]> ----
350
351    #[test]
352    fn bytes32_eq_array() {
353        let b = [0xffu8; 32];
354        assert_eq!(Value::Bytes32(b), b);
355    }
356
357    #[test]
358    fn identifier_eq_array_32() {
359        let id = [7u8; 32];
360        assert_eq!(Value::Identifier(id), id);
361    }
362
363    #[test]
364    fn bytes_eq_array_32() {
365        let data = [3u8; 32];
366        assert_eq!(Value::Bytes(data.to_vec()), data);
367    }
368
369    #[test]
370    fn non_bytes_ne_array_32() {
371        assert_ne!(Value::Null, [0u8; 32]);
372    }
373
374    // ---- PartialEq<[u8; 20]> ----
375
376    #[test]
377    fn bytes20_eq_array_20() {
378        let b = [1u8; 20];
379        assert_eq!(Value::Bytes20(b), b);
380    }
381
382    // ---- PartialEq<[u8; 36]> ----
383
384    #[test]
385    fn bytes36_eq_array_36() {
386        let b = [2u8; 36];
387        assert_eq!(Value::Bytes36(b), b);
388    }
389
390    // ---- PartialEq for &Value ----
391
392    #[test]
393    fn ref_value_eq_integer() {
394        let val = Value::U64(42);
395        assert_eq!(&val, 42u64);
396    }
397
398    #[test]
399    fn ref_value_eq_string() {
400        let val = Value::Text("hi".to_string());
401        assert_eq!(&val, "hi".to_string());
402    }
403
404    #[test]
405    fn ref_value_eq_str_ref() {
406        let val = Value::Text("hi".to_string());
407        assert_eq!(&val, "hi");
408    }
409
410    #[test]
411    fn ref_value_eq_float() {
412        let val = Value::Float(1.0);
413        assert_eq!(&val, 1.0f64);
414    }
415
416    #[test]
417    fn ref_value_eq_vec_u8() {
418        let val = Value::Bytes(vec![10, 20]);
419        assert_eq!(&val, vec![10u8, 20]);
420    }
421
422    #[test]
423    fn ref_value_eq_array_32() {
424        let b = [0u8; 32];
425        let val = Value::Bytes32(b);
426        assert_eq!(&val, b);
427    }
428
429    // ---- equal_underlying_data tests ----
430
431    #[test]
432    fn equal_underlying_data_bytes_vs_identifier_same_data() {
433        let data = [42u8; 32];
434        let bytes = Value::Bytes(data.to_vec());
435        let ident = Value::Identifier(data);
436        assert!(bytes.equal_underlying_data(&ident));
437        assert!(ident.equal_underlying_data(&bytes));
438    }
439
440    #[test]
441    fn equal_underlying_data_bytes_vs_identifier_different_data() {
442        let bytes = Value::Bytes(vec![0u8; 32]);
443        let ident = Value::Identifier([1u8; 32]);
444        assert!(!bytes.equal_underlying_data(&ident));
445    }
446
447    #[test]
448    fn equal_underlying_data_bytes32_vs_identifier() {
449        let data = [99u8; 32];
450        let b32 = Value::Bytes32(data);
451        let ident = Value::Identifier(data);
452        assert!(b32.equal_underlying_data(&ident));
453    }
454
455    #[test]
456    fn equal_underlying_data_bytes20_vs_bytes() {
457        let data = [5u8; 20];
458        let b20 = Value::Bytes20(data);
459        let bytes = Value::Bytes(data.to_vec());
460        assert!(b20.equal_underlying_data(&bytes));
461    }
462
463    #[test]
464    fn equal_underlying_data_u8_vs_u64_same_value() {
465        let a = Value::U8(10);
466        let b = Value::U64(10);
467        assert!(a.equal_underlying_data(&b));
468    }
469
470    #[test]
471    fn equal_underlying_data_i8_vs_i128_same_value() {
472        let a = Value::I8(-5);
473        let b = Value::I128(-5);
474        assert!(a.equal_underlying_data(&b));
475    }
476
477    #[test]
478    fn equal_underlying_data_u8_vs_u64_different_value() {
479        let a = Value::U8(10);
480        let b = Value::U64(20);
481        assert!(!a.equal_underlying_data(&b));
482    }
483
484    #[test]
485    fn equal_underlying_data_u16_vs_i32_same_value() {
486        let a = Value::U16(100);
487        let b = Value::I32(100);
488        assert!(a.equal_underlying_data(&b));
489    }
490
491    #[test]
492    fn equal_underlying_data_negative_i8_vs_u64() {
493        // negative can't match unsigned
494        let a = Value::I8(-1);
495        let b = Value::U64(255);
496        assert!(!a.equal_underlying_data(&b));
497    }
498
499    #[test]
500    fn equal_underlying_data_same_variant_same_value() {
501        let a = Value::U64(42);
502        let b = Value::U64(42);
503        assert!(a.equal_underlying_data(&b));
504    }
505
506    #[test]
507    fn equal_underlying_data_fallback_to_partial_eq() {
508        // Text vs Text uses default PartialEq
509        let a = Value::Text("hello".to_string());
510        let b = Value::Text("hello".to_string());
511        assert!(a.equal_underlying_data(&b));
512
513        let c = Value::Text("world".to_string());
514        assert!(!a.equal_underlying_data(&c));
515    }
516
517    #[test]
518    fn equal_underlying_data_null_vs_null() {
519        assert!(Value::Null.equal_underlying_data(&Value::Null));
520    }
521
522    #[test]
523    fn equal_underlying_data_different_types_not_equal() {
524        // A string vs a number should not be equal
525        let a = Value::Text("42".to_string());
526        let b = Value::U64(42);
527        assert!(!a.equal_underlying_data(&b));
528    }
529
530    #[test]
531    fn equal_underlying_data_bool_vs_bool() {
532        assert!(Value::Bool(true).equal_underlying_data(&Value::Bool(true)));
533        assert!(!Value::Bool(true).equal_underlying_data(&Value::Bool(false)));
534    }
535
536    #[test]
537    fn equal_underlying_data_float_vs_float() {
538        assert!(Value::Float(1.5).equal_underlying_data(&Value::Float(1.5)));
539        assert!(!Value::Float(1.5).equal_underlying_data(&Value::Float(2.5)));
540    }
541}