1 use proc_macro2::{Delimiter, TokenStream, TokenTree};
2 use std::hash::{Hash, Hasher};
3 
4 pub struct TokenTreeHelper<'a>(pub &'a TokenTree);
5 
6 impl<'a> PartialEq for TokenTreeHelper<'a> {
eq(&self, other: &Self) -> bool7     fn eq(&self, other: &Self) -> bool {
8         use proc_macro2::Spacing;
9 
10         match (self.0, other.0) {
11             (TokenTree::Group(g1), TokenTree::Group(g2)) => {
12                 match (g1.delimiter(), g2.delimiter()) {
13                     (Delimiter::Parenthesis, Delimiter::Parenthesis)
14                     | (Delimiter::Brace, Delimiter::Brace)
15                     | (Delimiter::Bracket, Delimiter::Bracket)
16                     | (Delimiter::None, Delimiter::None) => {}
17                     _ => return false,
18                 }
19 
20                 let s1 = g1.stream().into_iter();
21                 let mut s2 = g2.stream().into_iter();
22 
23                 for item1 in s1 {
24                     let item2 = match s2.next() {
25                         Some(item) => item,
26                         None => return false,
27                     };
28                     if TokenTreeHelper(&item1) != TokenTreeHelper(&item2) {
29                         return false;
30                     }
31                 }
32                 s2.next().is_none()
33             }
34             (TokenTree::Punct(o1), TokenTree::Punct(o2)) => {
35                 o1.as_char() == o2.as_char()
36                     && match (o1.spacing(), o2.spacing()) {
37                         (Spacing::Alone, Spacing::Alone) | (Spacing::Joint, Spacing::Joint) => true,
38                         _ => false,
39                     }
40             }
41             (TokenTree::Literal(l1), TokenTree::Literal(l2)) => l1.to_string() == l2.to_string(),
42             (TokenTree::Ident(s1), TokenTree::Ident(s2)) => s1 == s2,
43             _ => false,
44         }
45     }
46 }
47 
48 impl<'a> Hash for TokenTreeHelper<'a> {
hash<H: Hasher>(&self, h: &mut H)49     fn hash<H: Hasher>(&self, h: &mut H) {
50         use proc_macro2::Spacing;
51 
52         match self.0 {
53             TokenTree::Group(g) => {
54                 0u8.hash(h);
55                 match g.delimiter() {
56                     Delimiter::Parenthesis => 0u8.hash(h),
57                     Delimiter::Brace => 1u8.hash(h),
58                     Delimiter::Bracket => 2u8.hash(h),
59                     Delimiter::None => 3u8.hash(h),
60                 }
61 
62                 for item in g.stream() {
63                     TokenTreeHelper(&item).hash(h);
64                 }
65                 0xffu8.hash(h); // terminator w/ a variant we don't normally hash
66             }
67             TokenTree::Punct(op) => {
68                 1u8.hash(h);
69                 op.as_char().hash(h);
70                 match op.spacing() {
71                     Spacing::Alone => 0u8.hash(h),
72                     Spacing::Joint => 1u8.hash(h),
73                 }
74             }
75             TokenTree::Literal(lit) => (2u8, lit.to_string()).hash(h),
76             TokenTree::Ident(word) => (3u8, word).hash(h),
77         }
78     }
79 }
80 
81 pub struct TokenStreamHelper<'a>(pub &'a TokenStream);
82 
83 impl<'a> PartialEq for TokenStreamHelper<'a> {
eq(&self, other: &Self) -> bool84     fn eq(&self, other: &Self) -> bool {
85         let left = self.0.clone().into_iter().collect::<Vec<_>>();
86         let right = other.0.clone().into_iter().collect::<Vec<_>>();
87         if left.len() != right.len() {
88             return false;
89         }
90         for (a, b) in left.into_iter().zip(right) {
91             if TokenTreeHelper(&a) != TokenTreeHelper(&b) {
92                 return false;
93             }
94         }
95         true
96     }
97 }
98 
99 impl<'a> Hash for TokenStreamHelper<'a> {
hash<H: Hasher>(&self, state: &mut H)100     fn hash<H: Hasher>(&self, state: &mut H) {
101         let tts = self.0.clone().into_iter().collect::<Vec<_>>();
102         tts.len().hash(state);
103         for tt in tts {
104             TokenTreeHelper(&tt).hash(state);
105         }
106     }
107 }
108