shape/
hashing.rs

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