Skip to main content

graphql_tools/parser/schema/
hash.rs

1use crate::{
2    parser::{
3        hash::hash_list_unordered,
4        schema::{
5            Definition, DirectiveLocation, EnumTypeExtension, InputObjectTypeExtension,
6            InterfaceTypeExtension, ObjectTypeExtension, ScalarTypeExtension, TypeExtension,
7            UnionTypeExtension,
8        },
9    },
10    static_graphql::schema::{
11        DirectiveDefinition, Document, EnumType, EnumValue, Field, InputObjectType, InputValue,
12        InterfaceType, ObjectType, ScalarType, SchemaDefinition, TypeDefinition, UnionType,
13    },
14};
15use std::hash::Hash;
16
17impl Hash for Document {
18    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
19        "Document".hash(state);
20        hash_list_unordered(self.definitions.iter()).hash(state);
21    }
22}
23
24impl Hash for Definition<'static, String> {
25    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
26        match self {
27            Definition::SchemaDefinition(schema) => {
28                "Definition::SchemaDefinition".hash(state);
29                schema.hash(state);
30            }
31            Definition::TypeDefinition(type_def) => {
32                "Definition::TypeDefinition".hash(state);
33                type_def.hash(state);
34            }
35            Definition::TypeExtension(type_ext) => {
36                "Definition::TypeExtension".hash(state);
37                type_ext.hash(state);
38            }
39            Definition::DirectiveDefinition(directive_def) => {
40                "Definition::DirectiveDefinition".hash(state);
41                directive_def.hash(state);
42            }
43        }
44    }
45}
46
47impl Hash for TypeDefinition {
48    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
49        match self {
50            TypeDefinition::Scalar(scalar) => {
51                "TypeDefinition::Scalar".hash(state);
52                scalar.hash(state);
53            }
54            TypeDefinition::Object(object) => {
55                "TypeDefinition::Object".hash(state);
56                object.hash(state);
57            }
58            TypeDefinition::Interface(interface) => {
59                "TypeDefinition::Interface".hash(state);
60                interface.hash(state);
61            }
62            TypeDefinition::Union(union) => {
63                "TypeDefinition::Union".hash(state);
64                union.hash(state);
65            }
66            TypeDefinition::Enum(enum_) => {
67                "TypeDefinition::Enum".hash(state);
68                enum_.hash(state);
69            }
70            TypeDefinition::InputObject(input_object) => {
71                "TypeDefinition::InputObject".hash(state);
72                input_object.hash(state);
73            }
74        }
75    }
76}
77
78impl Hash for TypeExtension<'static, String> {
79    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
80        match self {
81            TypeExtension::Scalar(scalar) => {
82                "TypeExtension::Scalar".hash(state);
83                scalar.hash(state);
84            }
85            TypeExtension::Object(object) => {
86                "TypeExtension::Object".hash(state);
87                object.hash(state);
88            }
89            TypeExtension::Interface(interface) => {
90                "TypeExtension::Interface".hash(state);
91                interface.hash(state);
92            }
93            TypeExtension::Union(union) => {
94                "TypeExtension::Union".hash(state);
95                union.hash(state);
96            }
97            TypeExtension::Enum(enum_) => {
98                "TypeExtension::Enum".hash(state);
99                enum_.hash(state);
100            }
101            TypeExtension::InputObject(input_object) => {
102                "TypeExtension::InputObject".hash(state);
103                input_object.hash(state);
104            }
105        }
106    }
107}
108
109impl Hash for SchemaDefinition {
110    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
111        "SchemaDefinition".hash(state);
112        self.query.hash(state);
113        self.mutation.hash(state);
114        self.subscription.hash(state);
115        hash_list_unordered(self.directives.iter()).hash(state);
116    }
117}
118
119impl Hash for ScalarType {
120    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
121        "ScalarType".hash(state);
122        self.name.hash(state);
123        self.description.hash(state);
124        hash_list_unordered(self.directives.iter()).hash(state);
125    }
126}
127
128impl Hash for ScalarTypeExtension<'static, String> {
129    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
130        "ScalarTypeExtension".hash(state);
131        self.name.hash(state);
132        hash_list_unordered(self.directives.iter()).hash(state);
133    }
134}
135
136impl Hash for ObjectType {
137    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
138        "ObjectType".hash(state);
139        self.name.hash(state);
140        self.description.hash(state);
141        hash_list_unordered(self.implements_interfaces.iter()).hash(state);
142        hash_list_unordered(self.directives.iter()).hash(state);
143        hash_list_unordered(self.fields.iter()).hash(state);
144    }
145}
146
147impl Hash for ObjectTypeExtension<'static, String> {
148    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
149        "ObjectTypeExtension".hash(state);
150        self.name.hash(state);
151        hash_list_unordered(self.implements_interfaces.iter()).hash(state);
152        hash_list_unordered(self.directives.iter()).hash(state);
153        hash_list_unordered(self.fields.iter()).hash(state);
154    }
155}
156
157impl Hash for Field {
158    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
159        "Field".hash(state);
160        self.name.hash(state);
161        self.description.hash(state);
162        hash_list_unordered(self.arguments.iter()).hash(state);
163        self.field_type.hash(state);
164        hash_list_unordered(self.directives.iter()).hash(state);
165    }
166}
167
168impl Hash for InputValue {
169    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
170        "InputValue".hash(state);
171        self.name.hash(state);
172        self.description.hash(state);
173        self.value_type.hash(state);
174        self.default_value.hash(state);
175        hash_list_unordered(self.directives.iter()).hash(state);
176    }
177}
178
179impl Hash for InterfaceType {
180    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
181        "InterfaceType".hash(state);
182        self.name.hash(state);
183        self.description.hash(state);
184        hash_list_unordered(self.implements_interfaces.iter()).hash(state);
185        hash_list_unordered(self.directives.iter()).hash(state);
186        hash_list_unordered(self.fields.iter()).hash(state);
187    }
188}
189
190impl Hash for InterfaceTypeExtension<'static, String> {
191    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
192        "InterfaceTypeExtension".hash(state);
193        self.name.hash(state);
194        hash_list_unordered(self.implements_interfaces.iter()).hash(state);
195        hash_list_unordered(self.directives.iter()).hash(state);
196        hash_list_unordered(self.fields.iter()).hash(state);
197    }
198}
199
200impl Hash for UnionType {
201    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
202        "UnionType".hash(state);
203        self.name.hash(state);
204        self.description.hash(state);
205        hash_list_unordered(self.directives.iter()).hash(state);
206        hash_list_unordered(self.types.iter()).hash(state);
207    }
208}
209
210impl Hash for UnionTypeExtension<'static, String> {
211    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
212        "UnionTypeExtension".hash(state);
213        self.name.hash(state);
214        hash_list_unordered(self.directives.iter()).hash(state);
215        hash_list_unordered(self.types.iter()).hash(state);
216    }
217}
218
219impl Hash for EnumType {
220    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
221        "EnumType".hash(state);
222        self.name.hash(state);
223        self.description.hash(state);
224        hash_list_unordered(self.directives.iter()).hash(state);
225        hash_list_unordered(self.values.iter()).hash(state);
226    }
227}
228
229impl Hash for EnumValue {
230    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
231        "EnumValue".hash(state);
232        self.name.hash(state);
233        self.description.hash(state);
234        hash_list_unordered(self.directives.iter()).hash(state);
235    }
236}
237
238impl Hash for EnumTypeExtension<'static, String> {
239    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
240        "EnumTypeExtension".hash(state);
241        self.name.hash(state);
242        hash_list_unordered(self.directives.iter()).hash(state);
243        hash_list_unordered(self.values.iter()).hash(state);
244    }
245}
246
247impl Hash for InputObjectType {
248    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
249        "InputObjectType".hash(state);
250        self.name.hash(state);
251        self.description.hash(state);
252        hash_list_unordered(self.directives.iter()).hash(state);
253        hash_list_unordered(self.fields.iter()).hash(state);
254    }
255}
256
257impl Hash for InputObjectTypeExtension<'static, String> {
258    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
259        "InputObjectTypeExtension".hash(state);
260        self.name.hash(state);
261        hash_list_unordered(self.directives.iter()).hash(state);
262        hash_list_unordered(self.fields.iter()).hash(state);
263    }
264}
265
266impl Hash for DirectiveLocation {
267    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
268        "DirectiveLocation".hash(state);
269        self.as_str().hash(state);
270    }
271}
272
273impl Hash for DirectiveDefinition {
274    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
275        "DirectiveDefinition".hash(state);
276        self.name.hash(state);
277        self.description.hash(state);
278        hash_list_unordered(self.arguments.iter()).hash(state);
279        self.repeatable.hash(state);
280        hash_list_unordered(self.locations.iter()).hash(state);
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use std::hash::{DefaultHasher, Hash, Hasher};
287
288    use crate::parser::parse_schema;
289
290    #[test]
291    fn hashes_independent_from_position() {
292        let schema_a = parse_schema(
293            r#"
294            type Query {
295                field: String
296            }
297        "#,
298        )
299        .unwrap()
300        .into_static();
301        let schema_b = parse_schema(
302            r#"
303                type Query {
304            field: String
305            }
306        "#,
307        )
308        .unwrap()
309        .into_static();
310        let hash_a = {
311            let mut hasher = DefaultHasher::new();
312            schema_a.hash(&mut hasher);
313            hasher.finish()
314        };
315        let hash_b = {
316            let mut hasher = DefaultHasher::new();
317            schema_b.hash(&mut hasher);
318            hasher.finish()
319        };
320        assert_eq!(hash_a, hash_b);
321    }
322    #[test]
323    fn hashes_independent_from_definition_order() {
324        let schema_a = parse_schema(
325            r#"
326            type Query {
327                field: String
328            }
329            type Mutation {
330                field: String
331            }
332        "#,
333        )
334        .unwrap()
335        .into_static();
336        let schema_b = parse_schema(
337            r#"
338            type Mutation {
339                field: String
340            }
341            type Query {
342                field: String
343            }
344        "#,
345        )
346        .unwrap()
347        .into_static();
348        let hash_a = {
349            let mut hasher = DefaultHasher::new();
350            schema_a.hash(&mut hasher);
351            hasher.finish()
352        };
353        let hash_b = {
354            let mut hasher = DefaultHasher::new();
355            schema_b.hash(&mut hasher);
356            hasher.finish()
357        };
358        assert_eq!(hash_a, hash_b);
359    }
360    #[test]
361    fn hashes_independent_from_argument_order() {
362        let schema_a = parse_schema(
363            r#"
364            directive @test(arg1: String, arg2: String) on FIELD
365        "#,
366        )
367        .unwrap()
368        .into_static();
369        let schema_b = parse_schema(
370            r#"
371            directive @test(arg2: String, arg1: String) on FIELD
372        "#,
373        )
374        .unwrap()
375        .into_static();
376        let hash_a = {
377            let mut hasher = DefaultHasher::new();
378            schema_a.hash(&mut hasher);
379            hasher.finish()
380        };
381        let hash_b = {
382            let mut hasher = DefaultHasher::new();
383            schema_b.hash(&mut hasher);
384            hasher.finish()
385        };
386        assert_eq!(hash_a, hash_b);
387    }
388    #[test]
389    fn hashes_independent_from_directive_order() {
390        let schema_a = parse_schema(
391            r#"
392            type Query {
393                field: String @dir1 @dir2
394    }        "#,
395        )
396        .unwrap()
397        .into_static();
398        let schema_b = parse_schema(
399            r#"
400            type Query {
401                field: String @dir2 @dir1
402            }
403        "#,
404        )
405        .unwrap()
406        .into_static();
407        let hash_a = {
408            let mut hasher = DefaultHasher::new();
409            schema_a.hash(&mut hasher);
410            hasher.finish()
411        };
412        let hash_b = {
413            let mut hasher = DefaultHasher::new();
414            schema_b.hash(&mut hasher);
415            hasher.finish()
416        };
417        assert_eq!(hash_a, hash_b);
418    }
419    #[test]
420    fn hashes_independent_from_interface_order() {
421        let schema_a = parse_schema(
422            r#"
423            interface A {
424                field: String
425    }
426            interface B {
427                field: String
428            }
429            type Query implements A & B {
430                field: String
431            }
432        "#,
433        )
434        .unwrap()
435        .into_static();
436        let schema_b = parse_schema(
437            r#"
438            interface B {
439                field: String
440            }
441            interface A {
442                field: String
443            }
444            type Query implements B & A {
445                field: String
446            }
447        "#,
448        )
449        .unwrap()
450        .into_static();
451        let hash_a = {
452            let mut hasher = DefaultHasher::new();
453            schema_a.hash(&mut hasher);
454            hasher.finish()
455        };
456        let hash_b = {
457            let mut hasher = DefaultHasher::new();
458            schema_b.hash(&mut hasher);
459            hasher.finish()
460        };
461        assert_eq!(hash_a, hash_b);
462    }
463    #[test]
464    fn hashes_independent_from_enum_value_order() {
465        let schema_a = parse_schema(
466            r#"
467            enum MyEnum {
468                A
469                B
470    }
471        "#,
472        )
473        .unwrap()
474        .into_static();
475        let schema_b = parse_schema(
476            r#"
477            enum MyEnum {
478                B
479                A
480            }
481        "#,
482        )
483        .unwrap()
484        .into_static();
485        let hash_a = {
486            let mut hasher = DefaultHasher::new();
487            schema_a.hash(&mut hasher);
488            hasher.finish()
489        };
490        let hash_b = {
491            let mut hasher = DefaultHasher::new();
492            schema_b.hash(&mut hasher);
493            hasher.finish()
494        };
495        assert_eq!(hash_a, hash_b);
496    }
497    #[test]
498    fn hashes_independent_from_union_member_order() {
499        let schema_a = parse_schema(
500            r#"
501            union MyUnion = A | B
502        "#,
503        )
504        .unwrap()
505        .into_static();
506        let schema_b = parse_schema(
507            r#"
508            union MyUnion = B | A
509        "#,
510        )
511        .unwrap()
512        .into_static();
513        let hash_a = {
514            let mut hasher = DefaultHasher::new();
515            schema_a.hash(&mut hasher);
516            hasher.finish()
517        };
518        let hash_b = {
519            let mut hasher = DefaultHasher::new();
520            schema_b.hash(&mut hasher);
521            hasher.finish()
522        };
523        assert_eq!(hash_a, hash_b);
524    }
525    #[test]
526    fn hashes_independent_from_schema_definition_operation_order() {
527        let schema_a = parse_schema(
528            r#"
529            schema {
530                query: Query
531                mutation: Mutation
532                subscription: Subscription
533    }
534        "#,
535        )
536        .unwrap()
537        .into_static();
538        let schema_b = parse_schema(
539            r#"
540            schema {
541                subscription: Subscription
542                mutation: Mutation
543                query: Query
544            }
545        "#,
546        )
547        .unwrap()
548        .into_static();
549        let hash_a = {
550            let mut hasher = DefaultHasher::new();
551            schema_a.hash(&mut hasher);
552            hasher.finish()
553        };
554        let hash_b = {
555            let mut hasher = DefaultHasher::new();
556            schema_b.hash(&mut hasher);
557            hasher.finish()
558        };
559        assert_eq!(hash_a, hash_b);
560    }
561    #[test]
562    fn hashes_independent_from_field_order() {
563        let schema_a = parse_schema(
564            r#"
565            type Query {
566                field1: String
567                field2: String
568            }
569        "#,
570        )
571        .unwrap()
572        .into_static();
573        let schema_b = parse_schema(
574            r#"
575            type Query {
576                field2: String
577                field1: String
578            }
579        "#,
580        )
581        .unwrap()
582        .into_static();
583        let hash_a = {
584            let mut hasher = DefaultHasher::new();
585            schema_a.hash(&mut hasher);
586            hasher.finish()
587        };
588        let hash_b = {
589            let mut hasher = DefaultHasher::new();
590            schema_b.hash(&mut hasher);
591            hasher.finish()
592        };
593        assert_eq!(hash_a, hash_b);
594    }
595    #[test]
596    fn hashes_independent_from_input_value_order() {
597        let schema_a = parse_schema(
598            r#"
599            type Query {
600                field(input: InputType): String
601            }
602            input InputType {
603                arg1: String
604                arg2: String
605            }
606        "#,
607        )
608        .unwrap()
609        .into_static();
610        let schema_b = parse_schema(
611            r#"
612            type Query {
613                field(input: InputType): String
614            }
615            input InputType {
616                arg2: String
617                arg1: String
618            }
619        "#,
620        )
621        .unwrap()
622        .into_static();
623        let hash_a = {
624            let mut hasher = DefaultHasher::new();
625            schema_a.hash(&mut hasher);
626            hasher.finish()
627        };
628        let hash_b = {
629            let mut hasher = DefaultHasher::new();
630            schema_b.hash(&mut hasher);
631            hasher.finish()
632        };
633        assert_eq!(hash_a, hash_b);
634    }
635    #[test]
636    fn hashes_independent_from_directive_definition_location_order() {
637        let schema_a = parse_schema(
638            r#"
639            directive @test on FIELD | QUERY
640        "#,
641        )
642        .unwrap()
643        .into_static();
644        let schema_b = parse_schema(
645            r#"
646            directive @test on QUERY | FIELD
647        "#,
648        )
649        .unwrap()
650        .into_static();
651        let hash_a = {
652            let mut hasher = DefaultHasher::new();
653            schema_a.hash(&mut hasher);
654            hasher.finish()
655        };
656        let hash_b = {
657            let mut hasher = DefaultHasher::new();
658            schema_b.hash(&mut hasher);
659            hasher.finish()
660        };
661        assert_eq!(hash_a, hash_b);
662    }
663    #[test]
664    fn hashes_different_for_different_schemas() {
665        let schema_a = parse_schema(
666            r#"
667            type Query {
668                field: String
669            }
670        "#,
671        )
672        .unwrap()
673        .into_static();
674        let schema_b = parse_schema(
675            r#"
676            type Query {
677                field: Int
678            }
679        "#,
680        )
681        .unwrap()
682        .into_static();
683        let hash_a = {
684            let mut hasher = DefaultHasher::new();
685            schema_a.hash(&mut hasher);
686            hasher.finish()
687        };
688        let hash_b = {
689            let mut hasher = DefaultHasher::new();
690            schema_b.hash(&mut hasher);
691            hasher.finish()
692        };
693        assert_ne!(hash_a, hash_b);
694    }
695
696    #[test]
697    fn hashes_different_for_empty_and_same_two() {
698        let schema_a = parse_schema(
699            r#"
700            directive @tag repeatable on OBJECT
701            type Query @tag @tag {
702                f: String
703            }
704        "#,
705        )
706        .unwrap()
707        .into_static();
708        let schema_b = parse_schema(
709            r#"
710            directive @tag repeatable on OBJECT
711            type Query {
712                f: String
713            }
714        "#,
715        )
716        .unwrap()
717        .into_static();
718        let hash_a = {
719            let mut hasher = DefaultHasher::new();
720            schema_a.hash(&mut hasher);
721            hasher.finish()
722        };
723        let hash_b = {
724            let mut hasher = DefaultHasher::new();
725            schema_b.hash(&mut hasher);
726            hasher.finish()
727        };
728        assert_ne!(hash_a, hash_b);
729    }
730
731    #[test]
732    fn hashes_differently_when_a_xor_a() {
733        let schema_a = parse_schema(
734            r#"
735            directive @a repeatable on OBJECT
736            directive @b repeatable on OBJECT
737            directive @c repeatable on OBJECT
738            directive @d repeatable on OBJECT
739
740            type Query @a @a @b @c {
741                f: String
742            }
743        "#,
744        )
745        .unwrap()
746        .into_static();
747        let schema_b = parse_schema(
748            r#"
749            directive @a repeatable on OBJECT
750            directive @b repeatable on OBJECT
751            directive @c repeatable on OBJECT
752            directive @d repeatable on OBJECT
753
754            type Query @b @c @d @d {
755                f: String
756            }
757        "#,
758        )
759        .unwrap()
760        .into_static();
761        let hash_a = {
762            let mut hasher = DefaultHasher::new();
763            schema_a.hash(&mut hasher);
764            hasher.finish()
765        };
766        let hash_b = {
767            let mut hasher = DefaultHasher::new();
768            schema_b.hash(&mut hasher);
769            hasher.finish()
770        };
771        assert_ne!(hash_a, hash_b);
772    }
773}