1use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
8
9use std::iter::Peekable;
10
11pub struct Attribute {
12 pub name: String,
13 pub tokens: Vec<String>,
14}
15
16#[allow(dead_code)]
17pub enum Visibility {
18 Public,
19 Crate,
20 Restricted,
21 Private,
22}
23
24pub struct Field {
25 pub vis: Visibility,
26 pub field_name: Option<String>,
27 pub ty: Type,
28}
29
30#[allow(dead_code)]
31pub struct Type {
32 pub is_option: bool,
33 pub path: String,
34}
35
36pub struct Struct {
37 pub name: String,
38 pub tuple: bool,
39 pub fields: Vec<Field>,
40}
41
42pub struct EnumVariant {
43 pub name: String,
44 pub tuple: bool,
45 pub fields: Vec<Field>,
46}
47
48pub struct Enum {
49 pub name: String,
50 pub variants: Vec<EnumVariant>,
51}
52
53#[allow(dead_code)]
54pub enum Data {
55 Struct(Struct),
56 Enum(Enum),
57 Union(()),
58}
59
60pub fn next_visibility_modifier(
61 source: &mut Peekable<impl Iterator<Item = TokenTree>>,
62) -> Option<String> {
63 if let Some(TokenTree::Ident(ident)) = source.peek() {
64 if format!("{}", ident) == "pub" {
65 source.next();
66
67 if let Some(TokenTree::Group(group)) = source.peek() {
69 if group.delimiter() == Delimiter::Parenthesis {
70 next_group(source);
71 }
72 }
73
74 return Some("pub".to_string());
75 }
76 }
77
78 None
79}
80
81pub fn next_punct(source: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<String> {
82 if let Some(TokenTree::Punct(punct)) = source.peek() {
83 let punct = format!("{}", punct);
84 source.next();
85 return Some(punct);
86 }
87
88 None
89}
90
91pub fn next_exact_punct(
92 source: &mut Peekable<impl Iterator<Item = TokenTree>>,
93 pattern: &str,
94) -> Option<String> {
95 if let Some(TokenTree::Punct(punct)) = source.peek() {
96 let punct = format!("{}", punct);
97 if punct == pattern {
98 source.next();
99 return Some(punct);
100 }
101 }
102
103 None
104}
105
106pub fn next_eof<T: Iterator>(source: &mut Peekable<T>) -> Option<()> {
107 if source.peek().is_none() {
108 Some(())
109 } else {
110 None
111 }
112}
113
114pub fn next_ident(source: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<String> {
115 if let Some(TokenTree::Ident(ident)) = source.peek() {
116 let ident = format!("{}", ident);
117 source.next();
118 Some(ident)
119 } else {
120 None
121 }
122}
123
124pub fn next_group(source: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<Group> {
125 if let Some(TokenTree::Group(_)) = source.peek() {
126 let group = match source.next().expect("expected to read another token") {
127 TokenTree::Group(group) => group,
128 _ => unreachable!("just checked with peek()!"),
129 };
130 Some(group)
131 } else {
132 None
133 }
134}
135
136fn skip_doc_string(source: &mut Peekable<impl Iterator<Item = TokenTree>>) {
137 while skip_doc_string_inner(source) {}
138}
139
140fn skip_doc_string_inner(source: &mut Peekable<impl Iterator<Item = TokenTree>>) -> bool {
141 if next_exact_punct(source, "#").is_none() {
142 return false;
143 }
144 if next_group(source).is_none() {
145 return false;
146 }
147 true
148}
149
150#[allow(dead_code)]
151pub fn debug_current_token(source: &mut Peekable<impl Iterator<Item = TokenTree>>) {
152 println!("{:?}", source.peek());
153}
154
155fn next_type<T: Iterator<Item = TokenTree>>(source: &mut Peekable<T>) -> Option<Type> {
156 let group = next_group(source);
157 if let Some(group) = group {
158 let mut tuple_type = Type {
159 is_option: false,
160 path: "".to_string(),
161 };
162
163 while let Some(next_ty) = next_type(&mut group.stream().into_iter().peekable()) {
164 #[allow(clippy::format_push_string)]
165 tuple_type.path.push_str(&format!("{}, ", next_ty.path));
166 }
167
168 return Some(tuple_type);
169 }
170
171 let mut ty = next_ident(source)?;
173 while next_exact_punct(source, ":").is_some() {
174 let _second_colon = next_exact_punct(source, ":").expect("Expecting second :");
175
176 let next_ident = next_ident(source).expect("Expecting next path part after ::");
177 #[allow(clippy::format_push_string)]
178 ty.push_str(&format!("::{}", next_ident));
179 }
180
181 let angel_bracket = next_exact_punct(source, "<");
182 if angel_bracket.is_some() {
183 let mut generic_type = next_type(source).expect("Expecting generic argument");
184 while let Some(_comma) = next_exact_punct(source, ",") {
185 let next_ty = next_type(source).expect("Expecting generic argument");
186 #[allow(clippy::format_push_string)]
187 generic_type.path.push_str(&format!(", {}", next_ty.path));
188 }
189
190 let _closing_bracket =
191 next_exact_punct(source, ">").expect("Expecting closing generic bracket");
192
193 if ty == "Option" {
194 Some(Type {
195 path: generic_type.path,
196 is_option: true,
197 })
198 } else {
199 Some(Type {
200 path: format!("{}<{}>", ty, generic_type.path),
201 is_option: false,
202 })
203 }
204 } else {
205 Some(Type {
206 path: ty,
207 is_option: false,
208 })
209 }
210}
211
212fn next_fields(body: &mut Peekable<impl Iterator<Item = TokenTree>>, named: bool) -> Vec<Field> {
213 let mut fields = vec![];
214
215 loop {
216 if next_eof(body).is_some() {
217 break;
218 }
219
220 skip_doc_string(body);
221
222 let _visibility = next_visibility_modifier(body);
223 let field_name = if named {
224 let field_name = next_ident(body).expect("Field name expected");
225
226 let _ = next_exact_punct(body, ":").expect("Delimeter after field name expected");
227 Some(field_name)
228 } else {
229 None
230 };
231
232 let ty = next_type(body).expect("Expected field type");
233 let _punct = next_punct(body);
234
235 fields.push(Field {
236 vis: Visibility::Public,
237 field_name,
238 ty,
239 });
240 }
241 fields
242}
243
244fn next_struct(source: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Struct {
245 let struct_name = next_ident(source).expect("Unnamed structs are not supported");
246
247 let group = next_group(source);
248 if group.is_none() {};
250 if let Some(group) = group {
251 let delimiter = group.delimiter();
252 let tuple = match delimiter {
253 Delimiter::Parenthesis => true,
254 Delimiter::Brace => false,
255
256 _ => panic!("Struct with unsupported delimiter"),
257 };
258
259 let mut body = group.stream().into_iter().peekable();
260 let fields = next_fields(&mut body, !tuple);
261
262 if tuple {
263 next_exact_punct(source, ";").expect("Expected ; on the end of tuple struct");
264 }
265
266 Struct {
267 name: struct_name,
268 tuple,
269 fields,
270 }
271 } else {
272 let _ = next_punct(source);
274
275 Struct {
276 name: struct_name,
277 fields: vec![],
278 tuple: true,
279 }
280 }
281}
282
283fn next_enum(source: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Enum {
284 let enum_name = next_ident(source).expect("Unnamed enums are not supported");
285
286 let group = next_group(source);
287 if let Some(group) = group {
289 let mut body = group.stream().into_iter().peekable();
290
291 let mut variants = vec![];
292 loop {
293 if next_eof(&mut body).is_some() {
294 break;
295 }
296
297 skip_doc_string(&mut body);
298
299 let variant_name = next_ident(&mut body).expect("Unnamed variants are not supported");
300 let group = next_group(&mut body);
301
302 if let Some(group) = group {
303 let delimiter = group.delimiter();
304 let tuple = match delimiter {
305 Delimiter::Parenthesis => true,
306 Delimiter::Brace => false,
307
308 _ => panic!("Enum with unsupported delimiter"),
309 };
310 {
311 let mut body = group.stream().into_iter().peekable();
312 let fields = next_fields(&mut body, !tuple);
313 variants.push(EnumVariant {
314 name: variant_name,
315 tuple,
316 fields,
317 });
318 }
319 let _maybe_semicolon = next_exact_punct(&mut body, ";");
320 let _maybe_coma = next_exact_punct(&mut body, ",");
321 } else {
322 variants.push(EnumVariant {
323 name: variant_name,
324 tuple: true,
325 fields: vec![],
326 });
327 let _maybe_comma = next_exact_punct(&mut body, ",");
328 continue;
329 }
330 }
331
332 Enum {
333 name: enum_name,
334 variants,
335 }
336 } else {
337 Enum {
338 name: enum_name,
339 variants: vec![],
340 }
341 }
342}
343
344pub fn parse_data(input: TokenStream) -> Data {
345 let mut source = input.into_iter().peekable();
346
347 skip_doc_string(&mut source);
348
349 let pub_or_type = next_ident(&mut source).expect("Not an ident");
350
351 let type_keyword = if pub_or_type == "pub" {
352 next_ident(&mut source).expect("pub(whatever) is not supported yet")
353 } else {
354 pub_or_type
355 };
356
357 let res = match type_keyword.as_str() {
358 "struct" => Data::Struct(next_struct(&mut source)),
359 "enum" => {
360 let enum_ = next_enum(&mut source);
361 Data::Enum(enum_)
362 }
363 "union" => unimplemented!("Unions are not supported"),
364 unexpected => panic!("Unexpected keyword: {}", unexpected),
365 };
366
367 assert!(
368 source.next().is_none(),
369 "Unexpected data after end of the struct"
370 );
371
372 res
373}