shape/
hashing.rs

1use super::ShapeCase;
2use crate::case_enum::Error;
3use std::hash::Hash;
4use std::hash::Hasher;
5
6/// Because [`ShapeCase`] uses [`IndexMap`] and [`IndexSet`] for its Object, One, and All
7/// variants, the default derived Hash trait implementation does not work.
8/// Instead, we hash those cases manually, using a commutative operation to
9/// combine the order-insensitive hashes of the elements.
10impl Hash for ShapeCase {
11    fn hash<H: Hasher>(&self, state: &mut H) {
12        match self {
13            Self::Bool(value) => ("::Bool", value).hash(state),
14            Self::String(value) => ("::String", value).hash(state),
15            Self::Int(value) => ("::Int", value).hash(state),
16            Self::Float => "::Float".hash(state),
17            Self::Null => "::Null".hash(state),
18            Self::Array { prefix, tail } => ("::Array", prefix, tail).hash(state),
19            Self::Object { fields, rest } => {
20                // Hash each (key, value) pair and then combine the hashes with
21                // a commutative operation like XOR (without sorting the keys).
22                let fields_hash = fields.iter().fold(0, |acc, (key, value)| {
23                    let mut fields_hasher = std::collections::hash_map::DefaultHasher::new();
24                    key.hash(&mut fields_hasher);
25                    value.hash(&mut fields_hasher);
26                    // TODO Is XOR the best operation here?
27                    acc ^ fields_hasher.finish()
28                });
29                ("::Object", fields_hash, rest).hash(state);
30            }
31            Self::One(shapes) => {
32                let xor_hash = shapes.iter().fold(0, |acc, shape| {
33                    let mut shape_hasher = std::collections::hash_map::DefaultHasher::new();
34                    shape.hash(&mut shape_hasher);
35                    acc ^ shape_hasher.finish()
36                });
37                ("::One", xor_hash).hash(state);
38            }
39            Self::All(shapes) => {
40                let xor_hash = shapes.iter().fold(0, |acc, shape| {
41                    let mut shape_hasher = std::collections::hash_map::DefaultHasher::new();
42                    shape.hash(&mut shape_hasher);
43                    acc ^ shape_hasher.finish()
44                });
45                ("::All", xor_hash).hash(state);
46            }
47            Self::Name(name, subpath) => ("::Name", name, subpath).hash(state),
48            Self::Unknown => "::Unknown".hash(state),
49            Self::None => "::None".hash(state),
50            Self::Error(Error { message, partial }) => ("::Error", message, partial).hash(state),
51        }
52    }
53}