graphql_tools/parser/schema/
hash.rs1use 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}