1extern crate proc_macro;
2use proc_macro::{token_stream::IntoIter, Delimiter, Group, Spacing, TokenStream, TokenTree};
3use std::iter::FromIterator;
4
5#[proc_macro]
99pub fn tt_equal(item: TokenStream) -> TokenStream {
100 let (caller, lhs, rhs) = validate(item);
101
102 assert!(lhs.len() > 0);
103 assert!(rhs.len() > 0);
104
105 return_to_tt(
106 caller,
107 if lhs.len() == rhs.len() {
108 lhs.into_iter()
109 .zip(rhs.into_iter())
110 .all(|(lhs, rhs)| lhs.to_string().trim() == rhs.to_string().trim())
111 } else {
112 false
113 },
114 )
115}
116
117fn validate(item: TokenStream) -> (TokenTree, Vec<TokenTree>, Vec<TokenTree>) {
124 let mut iter = item.into_iter();
125
126 let caller = iter
127 .next()
128 .expect("'tt_equal' did not receive caller's tt bundle.");
129 let key = iter
130 .next()
131 .expect("'tt_equal' expects a key-value pair as input, but did not receive a key.");
132 if key.to_string().trim() != "input".to_string() {
133 panic!(
134 "'tt_equal' expects its input's key to be named 'input' but it was '{}'",
135 key.to_string().trim()
136 )
137 }
138 let separator = iter
139 .next()
140 .expect("'tt_equal' expects a key value pair as input but did not receive it.")
141 .to_string();
142 if separator != "=".to_string() {
143 panic!(
144 "'tt_equal' expects its input key-value pairs to be separated by a '=' \
145 but instead received '{}'",
146 separator
147 );
148 }
149 let value_group = iter
150 .next()
151 .expect("'tt_equal' expects a key-value pair as input but received no value.");
152 if iter.next().is_some() {
153 panic!("'tt_equal' expects only a key-value pair as input but received more.")
154 }
155 let mut unbracketed_group = expect_group(value_group, Delimiter::Bracket).into_iter();
156 let braced_group = unbracketed_group.next().expect(
157 "'tt_equal' expects its input value to be within '[{..}]' \
158 but the '{..}' was not given.",
159 );
160 if unbracketed_group.next().is_some() {
161 panic!(
162 "'tt_equal' expects its input value to be within '[{..}]' \
163 but it received additional tokens after the braces ('{..}')."
164 )
165 }
166 let mut clean_value = expect_group(braced_group, Delimiter::Brace).into_iter();
167 let lhs = get_next_joint_token(&mut clean_value)
168 .expect("'tt_equal' expects two token tree to compare but received none.");
169
170 let rhs = get_next_joint_token(&mut clean_value)
171 .expect("'tt_equal' expects two token tree to compare but received only one");
172 if let Some(x) = clean_value.next() {
173 panic!(
174 "'tt_equal' expects two token tree to compare but received more: '{:?} {:?} {:?}'",
175 lhs, rhs, x
176 )
177 }
178 (caller, lhs, rhs)
179}
180
181fn expect_group(tt: TokenTree, expected_delimiter: Delimiter) -> TokenStream {
186 if let TokenTree::Group(g) = tt {
187 if expected_delimiter == g.delimiter() {
188 g.stream()
189 } else {
190 panic!(
191 "'tt_equal' expects delimiter '{:?}' but got '{:?}'.",
192 expected_delimiter,
193 g.delimiter()
194 );
195 }
196 } else {
197 panic!(
198 "'tt_equal' expects a group of tokens inside {:?} but got '{:?}'",
199 expected_delimiter, tt
200 );
201 }
202}
203
204fn return_to_tt(caller: TokenTree, b: bool) -> TokenStream {
208 let return_call: TokenStream = "tt_call::tt_return!".parse().expect(
209 "'tt_equal' internal error 1. Please file a bug with the tt-equal crate maintainers.",
210 );
211 let return_value: TokenStream = format!("is_equal = [ {{ {} }} ]", b).parse().expect(
212 "'tt_equal' internal error 2. Please file a bug with the tt-equal crate maintainers.",
213 );
214
215 let mut return_body: Vec<_> = Vec::new();
216 return_body.push(caller);
217 return_body.extend(return_value);
218 let return_call_argument = TokenTree::from(Group::new(
219 Delimiter::Brace,
220 TokenStream::from_iter(return_body.into_iter()),
221 ));
222
223 let mut result: Vec<TokenTree> = Vec::new();
224 result.extend(return_call);
225 result.push(return_call_argument);
226
227 return TokenStream::from_iter(result.into_iter());
228}
229
230fn get_next_joint_token(stream: &mut IntoIter) -> Option<Vec<TokenTree>> {
244 let first = stream.next()?;
245 if let TokenTree::Punct(last) = first {
246 let mut tokens = vec![last];
247 while let Spacing::Joint = tokens.last().unwrap().spacing() {
248 let next = stream.next().unwrap();
249 if let TokenTree::Punct(p) = next {
250 tokens.push(p);
251 } else {
252 panic!(
253 "'tt_equal' encountered a Punct token joint with \
254 a non-Punct token: '{:?} {:?}'",
255 tokens, next
256 );
257 }
258 }
259 Some(tokens.into_iter().map(|p| TokenTree::Punct(p)).collect())
260 } else {
261 Some(vec![first])
262 }
263}