1#![doc = include_str!("../README.md")]
2use core::str::FromStr;
8use proc_macro::TokenTree::{Group, Ident, Punct};
9use proc_macro::{token_stream::IntoIter, Delimiter, Delimiter::Brace, Spacing, Span, TokenStream};
10use proc_macro::{Group as Gr, Ident as Idn, Punct as Pn};
11use std::iter::once;
12
13enum ParseStates {
14 Start,
15 Vis,
16 Name,
17 Args,
18 Minus,
19 Gt,
20 Out,
21}
22use ParseStates::{Args, Gt, Minus, Name, Out, Start, Vis};
23
24#[derive(Default)]
27struct Attr {
28 enum_name: String,
29 enum_ident: Option<Idn>,
30 run_method: String,
31 drv_dbg: bool,
32 out_ident: Option<Idn>,
33 out_dbg: bool,
34 strict_types: bool,
35}
36impl Attr {
37 fn new(attr_ts: TokenStream) -> Attr {
38 let mut attr_it = attr_ts.into_iter();
39 let attr = match [attr_it.next(), attr_it.next(), attr_it.next()] {
40 [Some(Ident(id)), Some(Punct(p)), Some(Ident(r_id))] if ",:".contains(p.as_char()) => {
41 Attr {
42 enum_name: id.to_string(),
43 enum_ident: Some(id),
44 run_method: r_id.to_string(),
45 drv_dbg: p.as_char() == ':',
46 ..Default::default()
47 }
48 }
49 _ => panic!("#[gen]: Syntax error in attribute #[methods_enum::gen(?? "),
50 };
51 match [attr_it.next(), attr_it.next()] {
52 [None, None] => attr,
53 [Some(Punct(p)), Some(Ident(out_id))] if ",=".contains(p.as_char()) => Attr {
54 out_ident: Some(out_id),
55 out_dbg: p.as_char() == '=',
56 strict_types: matches!(attr_it.next(), Some(Punct(p)) if p.as_char() == '!'),
57 ..attr
58 },
59 _ => panic!(
60 "#[gen]: Syntax error in attribute #[methods_enum::gen({}:{}??..",
61 attr.enum_name, attr.run_method
62 ),
63 }
64 }
65}
66
67#[derive(Default)]
68struct Meth {
69 ident: Option<Idn>,
70 prev_ts: TokenStream,
71 vis: TokenStream,
72 args: TokenStream,
73 params: String,
74 typs: String,
75 out_span: Option<Span>,
76 out: TokenStream,
77 body: TokenStream,
78}
79
80impl Meth {
81 fn args_parsing(&mut self, args_gr: Gr) -> ParseStates {
83 let mut args_it = args_gr.stream().into_iter();
84 let mut lg = 0;
85 let mut first = true;
86 let mut is_self = false;
87 self.params = String::new();
88 self.typs = String::new();
89 let st = loop {
90 match args_it.next() {
91 Some(Punct(p)) if p.as_char() == ',' && lg == 0 => {
92 match [args_it.next(), args_it.next()] {
93 [Some(Ident(id)), Some(Punct(p))] if p.as_char() == ':' => {
94 if first {
95 if !is_self {
96 break Start;
97 }
98 first = false;
99 } else {
100 self.params.push_str(", ");
101 self.typs.push_str(", ");
102 }
103 self.params.push_str(&id.to_string());
104 }
105 [Some(_), _] => break Start,
106 [None, _] => break if is_self { Minus } else { Start },
107 }
108 }
109 Some(Punct(p)) if "<>".contains(p.as_char()) => {
110 lg = lg + if p.as_char() == '<' { 1 } else { -1 };
111 self.typs.push(p.as_char());
112 }
113 Some(Ident(id)) if id.to_string() == "impl" => break Start,
114 Some(Ident(id)) if first && id.to_string() == "self" => is_self = true,
115 Some(Ident(id)) if !first && id.to_string() == "mut" => self.typs.push_str("mut "),
116 Some(tt) if !first => self.typs.push_str(&tt.to_string()),
117 None => break if is_self { Minus } else { Start },
118 _ => (),
119 }
120 };
121 if let Minus = st {
122 self.args = args_gr.stream();
123 self.out_span = None;
124 self.out = TokenStream::new();
125 }
126 self.prev_ts.extend(once(Group(args_gr)));
127 st
128 }
129
130 fn prev_extend(&mut self, tt: proc_macro::TokenTree, new_st: ParseStates) -> ParseStates {
131 self.prev_ts.extend(once(tt));
132 new_st
133 }
134
135 fn vec(iit: &mut IntoIter, attr: &Attr) -> Vec<Meth> {
136 let mut methods: Vec<Meth> = Vec::new();
137 let mut m = Meth::default();
138 let mut state = Start;
139 for tt in iit {
140 state = match (state, tt) {
141 (Start, Ident(id)) if id.to_string() == "pub" => {
142 m.vis.extend(once(Ident(id.clone())));
143 m.prev_extend(Ident(id), Vis)
144 }
145 (Vis, Group(gr)) if gr.delimiter() == Delimiter::Parenthesis => {
146 m.vis.extend(once(Group(gr.clone())));
147 m.prev_extend(Group(gr), Vis)
148 }
149 (st @ (Start | Vis), Ident(id)) if id.to_string() == "fn" => {
150 if let Start = st {
151 m.vis = TokenStream::new()
152 };
153 m.prev_extend(Ident(id), Name)
154 }
155 (Name, Ident(id)) => {
156 m.prev_ts.extend(once(Ident(id.clone())));
157 if id.to_string() == attr.run_method {
158 break;
159 }
160 m.ident = Some(id);
161 Args
162 }
163 (Args, Group(gr)) if gr.delimiter() == Delimiter::Parenthesis => m.args_parsing(gr),
164 (Minus, Punct(p)) if p.as_char() == '-' => m.prev_extend(Punct(p), Gt),
165 (Gt, Punct(p)) if p.as_char() == '>' => {
166 m.out_span = Some(p.span());
167 m.prev_extend(Punct(p), Out)
168 }
169 (Out, Group(gr)) if gr.delimiter() == Brace && attr.out_ident.is_none() => {
170 m.prev_extend(Group(gr), Start) }
172 (Minus, Group(gr)) if gr.delimiter() == Brace => m.prev_extend(Group(gr), Start),
173 (Out, Ident(id)) if id.to_string() == "where" => m.prev_extend(Ident(id), Start),
174 (Minus | Out, Punct(p)) if p.as_char() == ';' => {
175 methods.push(m);
176 m = Meth::default();
177 Start
178 }
179 (Out, Group(gr)) if gr.delimiter() == Brace => {
180 m.body = gr.stream();
181 methods.push(m);
182 m = Meth::default();
183 Start
184 }
185 (Out, tt) => {
186 m.out.extend(TokenStream::from(tt.clone()));
187 m.prev_extend(tt, Out)
188 }
189 (_, tt) => m.prev_extend(tt, Start),
190 }
191 }
192 m.ident = None;
193 methods.push(m);
194 methods
195 }
196}
197
198fn ts_to_doc(ts: &TokenStream) -> String {
199 let s = ts.to_string().replace("& ", "&").replace(":: ", "::");
200 let inds: Vec<_> = s.match_indices(&['!', '(', ',', ':', '<', '>']).map(|t| t.0).collect();
201 ([0].iter().chain(inds.iter()))
202 .zip(inds.iter().chain(&[s.len()]))
203 .map(|(&a, &b)| s[a..b].trim_end())
204 .collect()
205}
206
207#[doc = include_str!("gen_details.md")]
307#[proc_macro_attribute]
308pub fn gen(attr_ts: TokenStream, item_ts: TokenStream) -> TokenStream {
309 let attr = Attr::new(attr_ts);
312
313 let mut item_it = item_ts.into_iter();
314
315 let mut item_ts = TokenStream::from_iter(
316 item_it.by_ref().take_while(|tt| !matches!(tt, Ident(id) if id.to_string() == "impl")),
317 );
318 item_ts.extend(once(Ident(Idn::new("impl", Span::call_site()))));
319
320 let mut block_it = match [item_it.next(), item_it.next(), item_it.next()] {
321 [Some(Ident(item_n)), Some(Group(gr)), None] if gr.delimiter() == Brace => {
322 item_ts.extend(once(Ident(item_n)));
323 gr.stream().into_iter()
324 }
325 m => panic!(
326 "#[gen]: SYNTAX ERROR
327'attribute #[gen] must be set on block impl without treyds and generics': {m:?}"
328 ),
329 };
330
331 let methods = Meth::vec(&mut block_it, &attr);
332
333 let head = r##"
334 #[derive(Debug)]
335 #[allow(non_camel_case_types)]
336 /// Formed by macro [`#[methods_enum::gen(...)]`](https://docs.rs/methods-enum):
337 /// ```
338 /// #[derive(Debug)]
339 /// #[allow(non_camel_case_types)]
340 #[doc = "enum "##;
341 let head_w_o_dbg = head.lines().filter(|s| !s.ends_with("g)]")).collect::<Vec<_>>().join("\n");
342 let mut outs: Vec<(String, String, Span)> = Vec::new();
344 let mut enum_doc = " {".to_string();
345 let mut enum_ts = TokenStream::new();
346 for m in methods.iter() {
347 if let Some(ident) = &m.ident {
348 enum_ts.extend(once(Ident(ident.clone())));
349 let typs = m.typs.replace('&', "&'a ");
350 enum_ts.extend(TokenStream::from_str(&format!("({typs}), ")));
351 enum_doc.push_str(&format!("\n {ident}({typs}), "));
352 if let Some(out_span) = m.out_span {
353 outs.push((ident.to_string(), ts_to_doc(&m.out), out_span));
354 }
355 }
356 }
357 let lftm = if enum_doc.contains('&') { "<'a>" } else { "" };
358 enum_doc.push_str("\n}\n```\n---\nMethod bodies generated by the same macro:\n```");
359
360 let is_result = attr.out_ident.is_none() && outs.iter().any(|t| t.1.contains("Result<"));
361 let self_run_enum = format!("self.{}({}::", attr.run_method, attr.enum_name);
362 let mut methods_ts = TokenStream::new();
363 for m in methods {
364 methods_ts.extend(m.prev_ts);
365 if let Some(ident) = m.ident {
366 enum_doc.push_str(&format!(
367 "\n{}fn {ident}({})",
368 (ts_to_doc(&m.vis) + " ").trim_start(),
369 ts_to_doc(&m.args)
370 ));
371 let mut body_ts = TokenStream::new();
372 let out = if m.out.is_empty() {
373 enum_doc.push_str(" {");
374 if is_result {
375 enum_doc.push_str("\n #![allow(unused_must_use)]");
376 body_ts.extend(TokenStream::from_str("#![allow(unused_must_use)]").unwrap());
377 }
378 String::new()
379 } else {
380 let name = ident.to_string();
381 let find_out = outs.iter().find(|t| t.0 == name).unwrap().1.clone();
382 enum_doc.push_str(&format!(" -> {find_out} {{"));
383 find_out
384 };
385 let call_run = format!("{self_run_enum}{ident}({}))", m.params);
386 if attr.out_ident.is_none() || m.out.is_empty() {
387 enum_doc.push_str(&format!("\n {call_run}"));
388 body_ts.extend(TokenStream::from_str(&call_run).unwrap());
389 if m.out.is_empty() {
390 enum_doc.push_str(";");
391 body_ts.extend(once(Punct(Pn::new(';', Spacing::Alone))));
392 }
393 } else if let Some(out_ident) = &attr.out_ident {
394 enum_doc.push_str(&format!("\n match {call_run} {{"));
395 body_ts.extend(TokenStream::from_str(&format!("match {call_run}")).unwrap());
396 let out_enum = out_ident.to_string() + "::";
397 let varname = format!("_{}", out_ident).to_lowercase();
398 let lside = if attr.strict_types {
399 format!("{out_enum}{ident}(x)")
400 } else {
401 (outs.iter())
402 .filter_map(|(n, o, _)| (o == &out).then(|| out_enum.clone() + n + "(x)"))
403 .reduce(|s, n| s + " | " + &n)
404 .unwrap()
405 };
406 enum_doc.push_str(&format!("\n {lside} => x,\n {varname} => "));
407 let mut match_ts =
408 TokenStream::from_str(&format!("{lside} => x, {varname} => ")).unwrap();
409 if m.body.is_empty() {
410 let panic_s = format!(
411 "panic!(\"Type mismatch in the {ident}() method:
412 expected- {},
413 found- {out_enum}{{}}\", {varname}.stype())",
414 lside
415 .replace("(x)", &format!("({out})"))
416 .replace(" | ", "\n | ")
417 );
418 enum_doc.push_str(&panic_s);
419 match_ts.extend(TokenStream::from_str(&panic_s).unwrap());
420 } else {
421 enum_doc.push_str(
422 &ts_to_doc(&m.body)
423 .replace(" {", " {\n ")
424 .replace(", _ =>", ",\n _ =>"),
425 );
426 match_ts.extend(m.body);
427 }
428 enum_doc.push_str("\n }");
429 body_ts.extend(once(Group(Gr::new(Brace, match_ts))));
430 }
431 enum_doc.push_str("\n}");
432 methods_ts.extend(once(Group(Gr::new(Brace, body_ts))));
433 }
434 }
435 methods_ts.extend(block_it);
436 item_ts.extend(once(Group(Gr::new(Brace, methods_ts))));
437
438 let mut res_ts = TokenStream::from_str(&format!(
439 "{}{}{lftm}{}\"] enum ",
440 if attr.drv_dbg { head } else { &head_w_o_dbg },
441 attr.enum_name,
442 (enum_doc + "\n```").escape_debug().to_string()
443 ))
444 .unwrap();
445 res_ts.extend(once(Ident(attr.enum_ident.unwrap())));
446 res_ts.extend(TokenStream::from_str(lftm).unwrap());
447 res_ts.extend(once(Group(Gr::new(Brace, enum_ts))));
448
449 res_ts.extend(item_ts);
450
451 if let Some(out_ident) = &attr.out_ident {
452 enum_doc = " {\n Unit,".to_string();
453 enum_ts = TokenStream::from_str("Unit, ").unwrap();
454 let indent = "\n ";
455 let mut stype = format!(
456 " fn stype(&self) -> &'static str {{
457 match self {{{indent}{out_ident}::Unit => \"Unit\","
458 );
459 let mut lftm = "";
460 for (name, mut out, span) in outs {
461 enum_ts.extend(once(Ident(Idn::new(&name, span))));
462 stype.push_str(&format!("{indent}{out_ident}::{name}(..) => \"{name}({out})\","));
463 if out.contains('&') {
464 lftm = "<'a>";
465 out = out.replace('&', "&'a ");
466 }
467 enum_ts.extend(TokenStream::from_str(&format!("({out}), ")));
468 enum_doc.push_str(&format!("\n {name}({out}), "));
469 }
470 stype = format!("impl{lftm} {out_ident}{lftm} {{\n{stype}\n }}\n }}\n}}");
471 enum_doc = (enum_doc + "\n}\n\n" + &stype + "\n```").escape_debug().to_string();
472
473 res_ts.extend(TokenStream::from_str(&format!(
474 "{}{out_ident}{lftm}{enum_doc}\"] enum ",
475 if attr.out_dbg { head } else { &head_w_o_dbg }
476 )));
477 res_ts.extend(once(Ident(out_ident.clone())));
478 res_ts.extend(TokenStream::from_str(lftm).unwrap());
479 res_ts.extend(once(Group(Gr::new(Brace, enum_ts))));
480 res_ts.extend(TokenStream::from_str(&stype).unwrap());
481 }
482
483 if std::env::var("M_ENUM_DBG").map_or(false, |v| &v != "0") {
484 println!(
485 "\nM_ENUM_DBG - output to compiler input for enum {}:\n{}\n",
486 attr.enum_name, res_ts
487 );
488 }
489
490 res_ts
491}
492
493use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
500use std::hash::{Hash, Hasher};
501use std::mem;
502
503struct Flags {
504 panic: bool,
505 no_semnt: bool,
506}
507
508#[derive(Default)]
509struct Item {
510 name: String,
511 ident: Option<Idn>, it_enum: bool,
513 no_def: bool,
514 prev_ts: TokenStream,
515 group: TokenStream,
516 methods: Vec<MethIM>,
517}
518impl Item {
519 fn prev_extend(&mut self, tt: proc_macro::TokenTree, new_state: ParseStates) -> ParseStates {
520 if !self.no_def {
521 self.prev_ts.extend(once(tt))
522 }
523 if let Vis = new_state {
524 self.ident = None;
525 self.name = String::new();
526 Name
527 } else {
528 new_state
529 }
530 }
531
532 fn vec(ts: TokenStream) -> (Vec<Item>, HashMap<String, bool>, Flags) {
533 let mut items = Vec::new();
534 let mut mmap: HashMap<String, bool> = HashMap::new(); let mut impl_n = String::new();
536 let mut item = Item::default();
537 let mut lg = 0;
538 let mut state = Args;
539 let mut flags = Flags { no_semnt: true, panic: true };
540 if cfg!(debug_assertions) {
541 flags.no_semnt = false;
542 flags.panic = false;
543 }
544 for tt in ts {
545 state = match (state, tt, lg) {
546 (Args, Group(gr), 0) if gr.delimiter() == Delimiter::Parenthesis => {
547 if cfg!(debug_assertions) {
548 for fl in gr.stream() {
549 match fl {
550 Punct(p) if p.as_char() == '!' => flags.panic = true,
551 Ident(id) => match &id.to_string().to_lowercase()[..] {
552 "ns" | "sn" => {
553 flags.no_semnt = true;
554 flags.panic = true;
555 }
556 _ => (),
557 },
558 _ => (),
559 }
560 }
561 }
562 Start
563 }
564 (Start | Args, Punct(p), 0) if p.as_char() == '@' => {
565 item.it_enum = true;
566 item.no_def = true;
567 item.prev_extend(Punct(p), Vis)
568 }
569 (Start | Args, Ident(id), 0) => match &id.to_string()[..] {
570 "impl" => item.prev_extend(Ident(id), Vis),
571 "enum" => {
572 item.it_enum = true;
573 item.prev_extend(Ident(id), Vis)
574 }
575 _ => item.prev_extend(Ident(id), Start),
576 },
577 (Name, Ident(id), 0) if id.to_string() == "for" => item.prev_extend(Ident(id), Out),
578 (st @ (Name | Out), Ident(id), 0) => {
579 match st {
580 Name => item.ident = Some(id.clone()),
581 _ => item.name = id.to_string(),
582 }
583 item.prev_extend(Ident(id), st)
584 }
585 (Name | Out, Group(gr), 0) if gr.delimiter() == Brace => {
586 if item.ident.is_some() {
587 if item.it_enum {
588 item.group = gr.stream();
589 item.name = item.ident.as_ref().unwrap().to_string();
590 items.push(mem::take(&mut item));
591 } else {
592 if item.name.is_empty() {
593 item.name = item.ident.as_ref().unwrap().to_string();
594 item.ident = None;
595 }
596 if impl_n.is_empty() || impl_n == item.name {
597 if impl_n.is_empty() {
598 impl_n = item.name.clone();
599 }
600 item.fill_methods(gr.stream(), &mut mmap);
601 items.push(mem::take(&mut item));
602 } else {
603 item.prev_ts.extend(once(Group(gr)));
604 }
605 }
606 }
607 Start
608 }
609 (st, Punct(p), _) if "<>".contains(p.as_char()) => {
610 lg = 0.max(lg + if p.as_char() == '<' { 1 } else { -1 });
611 item.prev_extend(Punct(p), st)
612 }
613 (Args, tt, _) => item.prev_extend(tt, Start),
614 (st, tt, _) => item.prev_extend(tt, st),
615 }
616 }
617 item.name = String::new();
618 items.push(item);
619 (items, mmap, flags)
620 }
621
622 fn fill_methods(&mut self, ts: TokenStream, mmap: &mut HashMap<String, bool>) {
623 let mut m = MethIM::default();
624 let mut args: Option<TokenStream> = None;
625 let mut state = Start;
626 for tt in ts {
627 state = match (state, tt) {
628 (Start, Ident(id)) if id.to_string() == "fn" => m.prev_extend(Ident(id), Name),
629 (Name, Ident(id)) => {
630 m.name = self.ident.as_ref().map_or(id.to_string(), |t| format!("{id}() {t}"));
631 args = None;
632 m.prev_extend(Ident(id), Args)
633 }
634 (Args, Punct(p)) if p.as_char() == '<' => {
635 args = Some(TokenStream::from_iter(once(Ident(Idn::new("impl", p.span())))));
636 m.prev_extend(Punct(p), Gt)
637 }
638 (Args, Group(gr)) if gr.delimiter() == Delimiter::Parenthesis => {
639 args = Some(gr.stream());
640 m.prev_extend(Group(gr), Gt)
641 }
642 (Gt, Group(gr)) if gr.delimiter() == Brace => m.prev_extend(Group(gr), Start),
643 (Gt, Punct(p)) if p.as_char() == ';' => m.prev_extend(Punct(p), Start),
644 (Gt, Punct(p)) if p.as_char() == '~' => Out,
645 (Gt | Args, tt) => m.prev_extend(tt, Gt),
646 (Out, Group(gr)) if gr.delimiter() == Brace => {
647 if m.found_match(&gr) {
648 mmap.insert(
649 m.name.clone(), args.take().map_or(false, |t| {
651 t.into_iter()
652 .any(|tr| matches!(tr, Ident(id) if id.to_string() == "impl"))
653 }),
654 );
655 self.methods.push(mem::take(&mut m));
656 } else {
657 m.prev_ts.extend(once(Group(gr)))
658 }
659 Start
660 }
661 (_, tt) => m.prev_extend(tt, Start),
662 }
663 }
664 m.name = String::new();
665 self.methods.push(m);
666 }
667}
668
669#[derive(Default)]
670struct MethIM {
671 name: String,
672 prev_ts: TokenStream,
673 body: TokenStream,
674 dflt_arm: Option<Gr>,
675 tail: TokenStream,
676}
677impl MethIM {
678 fn prev_extend(&mut self, tt: proc_macro::TokenTree, new_st: ParseStates) -> ParseStates {
679 self.prev_ts.extend(once(tt));
680 new_st
681 }
682
683 fn found_match(&mut self, body: &Gr) -> bool {
684 self.body = TokenStream::new();
685 let mut iit = body.stream().into_iter();
686 let mut found = false;
687 while let Some(tt) = iit.next() {
688 match (found, tt) {
689 (false, Ident(id)) if id.to_string() == "match" => {
690 self.body.extend(once(Ident(id)));
691 found = true;
692 }
693 (true, Punct(p)) if p.as_char() == ';' => {
694 self.tail.extend(once(Punct(p)).chain(iit));
695 return true;
696 }
697 (true, Group(gr)) if gr.delimiter() == Brace => {
698 let mut isfat_arrow = false;
699 let mut gr_iit = gr.stream().into_iter();
700 while let Some(tt) = gr_iit.next() {
701 if let Punct(p) = tt {
702 if p.as_char() == '=' {
703 if let Some(Punct(gt)) = gr_iit.next() {
704 if gt.as_char() == '>' {
705 isfat_arrow = true;
706 break;
707 }
708 }
709 }
710 }
711 }
712 if isfat_arrow {
713 self.body.extend(once(Group(gr)));
714 found = false;
715 } else {
716 self.dflt_arm = Some(gr);
717 self.tail.extend(iit);
718 return true;
719 }
720 }
721 (_, tt) => self.body.extend(once(tt)),
722 }
723 }
724 found
725 }
726}
727
728struct VarMeth {
729 ident: Idn,
730 fields: Option<Gr>,
731 block: Gr,
732 opt_trait: Option<Idn>,
733}
734
735#[derive(Default)]
736struct Var {
737 ident: Option<Idn>,
738 fields: Option<Gr>,
739 methods: HashMap<String, VarMeth>,
740}
741impl Var {
742 fn vec(item: &mut Item) -> (Vec<Var>, String) {
743 let mut iit = mem::take(&mut item.group).into_iter();
744 let mut enm: Vec<Var> = Vec::new();
745 let mut err = String::new();
746 let mut err_state = false;
747 let dd = TokenStream::from_str("..").unwrap();
748 let mut var = Var::default();
749 while let Some(tt) = iit.next() {
750 if err_state {
751 match tt {
752 Punct(p) if p.as_char() == ',' => {
753 err_state = false;
754 item.group.extend(once(Punct(p)));
755 enm.push(mem::take(&mut var));
756 }
757 _ => (),
758 }
759 } else {
760 match tt {
761 Punct(p) if p.as_char() == '#' && var.ident.is_none() => match iit.next() {
762 Some(Group(gr)) if gr.delimiter() == Delimiter::Bracket => {
763 item.group.extend([Punct(p), Group(gr)]);
764 }
765 Some(Punct(p1)) if p.as_char() == '!' => match iit.next() {
766 Some(Group(gr)) if gr.delimiter() == Delimiter::Bracket => {
767 item.group.extend([Punct(p), Punct(p1), Group(gr)]);
768 }
769 _ => (),
770 },
771 _ => (),
772 },
773 Ident(id) => {
774 if var.ident.is_none() {
775 var.ident = Some(id.clone());
776 item.group.extend(once(Ident(id)));
777 } else {
779 let mut opt_tt = iit.next();
781 match opt_tt {
782 Some(Group(ref g)) if g.delimiter() == Delimiter::Parenthesis => {
783 opt_tt = iit.next()
784 }
785 _ => (),
786 }
787 let opt_trait = match opt_tt {
788 Some(Ident(trait_id)) => {
789 opt_tt = iit.next();
790 Some(trait_id)
791 }
792 _ => None,
793 };
794 let in_enum_var =
795 format!("in `enum {}::{}`", item.name, var.ident.as_ref().unwrap());
796 match opt_tt {
797 Some(Group(block)) if block.delimiter() == Brace => {
798 let name = (opt_trait.as_ref())
799 .map_or(id.to_string(), |t| format!("{id}() {t}"));
800 let m = VarMeth {
801 ident: id,
802 fields: var.fields.clone(),
803 block,
804 opt_trait,
805 };
806 if var.methods.insert(name.clone(), m).is_some() {
807 err += &format!(
808 "\nRepetition of method name `{name}` \
809{in_enum_var} (last arm-block used)"
810 );
811 }
812 }
813 Some(tt2) => {
814 err += &format!(
815 "\nInvalid syntax in method `{id}` {in_enum_var} \
816- expected arm-block: `{{...}}`, found: `{tt2}`"
817 );
818 err_state = true;
819 }
820 None => {
821 err += &format!(
822 "\nUnexpected end of macro on method`{id}` {in_enum_var}"
823 );
824 err_state = true;
825 }
826 };
827 }
828 }
829 Group(gr) if gr.delimiter() != Delimiter::Bracket => {
830 match (var.methods.is_empty(), var.fields.is_none()) {
831 (true, true) => {
832 var.fields = Some(Gr::new(gr.delimiter(), dd.clone()));
833 item.group.extend(once(Group(gr)));
834 }
835 (_, false) => var.fields = Some(gr),
836 _ => (),
837 }
838 }
839 Punct(p) if p.as_char() == ',' => {
840 if var.ident.is_some() {
841 item.group.extend(once(Punct(p)));
842 enm.push(mem::take(&mut var));
843 }
844 }
845 _ => (),
846 }
847 }
848 }
849
850 if var.ident.is_some() {
851 enm.push(var)
852 }
853 (enm, err)
854 }
855}
856
857#[doc = include_str!("impl_match_details.md")]
974#[proc_macro]
975pub fn impl_match(input_ts: TokenStream) -> TokenStream {
976 let (mut items, mmap, flags) = Item::vec(input_ts);
979 let opt_enm_idx = (items.iter().enumerate().find_map(|(i, it)| it.no_def.then(|| i)))
980 .or_else(|| items.iter().enumerate().find_map(|(i, it)| it.it_enum.then(|| i)));
981 let ((mut enm, mut err), enm_i) =
982 opt_enm_idx.map_or(((Vec::new(), String::new()), None), |i| {
983 let enm_it = items.get_mut(i).unwrap();
984 (Var::vec(enm_it), enm_it.ident.take())
985 });
986 let fat_arrow = TokenStream::from_str("=>").unwrap();
987 let empty_gr = Gr::new(Brace, TokenStream::new());
988 let dd = TokenStream::from_str("..").unwrap();
989 let dd_gr = |g: &Gr| Gr::new(g.delimiter(), dd.clone());
990
991 let mut res_ts = TokenStream::new();
992 for item in items.iter_mut() {
993 res_ts.extend(mem::take(&mut item.prev_ts));
994 if !item.name.is_empty() && !item.no_def {
995 let group = if item.it_enum {
996 mem::take(&mut item.group)
997 } else {
998 let mut group = TokenStream::new();
999 for mut m in mem::take(&mut item.methods) {
1000 group.extend(m.prev_ts);
1001 if !m.name.is_empty() {
1002 let mut match_block = TokenStream::new();
1003 for var in enm.iter_mut() {
1004 let (fields, arm_block) = match var.methods.get_mut(&m.name) {
1005 Some(VarMeth { fields, block, .. }) => {
1006 (fields.take(), mem::replace(block, empty_gr.clone()))
1007 }
1008 None => {
1009 if m.dflt_arm.is_none() {
1010 continue;
1011 }
1012 (var.fields.as_ref().map(dd_gr), m.dflt_arm.clone().unwrap())
1013 }
1014 };
1015 match_block.extend(TokenStream::from_iter([
1016 Ident(enm_i.as_ref().unwrap().clone()),
1017 Punct(Pn::new(':', Spacing::Joint)),
1018 Punct(Pn::new(':', Spacing::Alone)),
1019 Ident(var.ident.as_ref().unwrap().clone()),
1020 ]));
1021 match_block.extend(fields.map(Group));
1022 match_block.extend(fat_arrow.clone());
1023 match_block.extend(once(Group(arm_block)));
1024 }
1025 m.body.extend(once(Group(Gr::new(Brace, match_block))).chain(m.tail));
1026 group.extend(once(Group(Gr::new(Brace, m.body))));
1027 }
1028 }
1029 group
1030 };
1031 res_ts.extend(once(Group(Gr::new(Brace, group))));
1032 }
1033 }
1034
1035 if !flags.no_semnt {
1037 if enm_i.is_some() {
1038 let item_n = (items.iter())
1039 .find_map(|it| (!it.it_enum && !it.name.is_empty()).then(|| it.name.clone()))
1040 .unwrap_or_default();
1041 let span = Span::call_site();
1042 let item_ts = TokenStream::from_iter([
1043 Ident(Idn::new(&item_n, span)),
1044 Punct(Pn::new(':', Spacing::Joint)),
1045 Punct(Pn::new(':', Spacing::Alone)),
1046 ]);
1047 let sm = Punct(Pn::new(';', Spacing::Alone));
1048 let mut fn_ts = TokenStream::new();
1049 if !item_n.is_empty() {
1050 for var in enm.iter_mut() {
1051 for (k, m) in var.methods.iter_mut() {
1052 if !mmap.get(k).map_or(false, |&v| v) {
1053 fn_ts.extend(if let Some(trait_i) = m.opt_trait.take() {
1054 TokenStream::from_iter([
1055 Punct(Pn::new('<', Spacing::Alone)),
1056 Ident(Idn::new(&item_n, span)),
1057 Ident(Idn::new("as", span)),
1058 Ident(trait_i),
1059 Punct(Pn::new('>', Spacing::Alone)),
1060 Punct(Pn::new(':', Spacing::Joint)),
1061 Punct(Pn::new(':', Spacing::Alone)),
1062 ])
1063 } else {
1064 item_ts.clone()
1065 });
1066 fn_ts.extend([Ident(m.ident.clone()), sm.clone()]);
1067 }
1068 }
1069 }
1070 }
1071 if !fn_ts.is_empty() {
1072 let mut hasher = DefaultHasher::new();
1073 (item_n + "-" + mmap.keys().next().unwrap_or(&String::new())).hash(&mut hasher);
1074 res_ts.extend(
1075 TokenStream::from_str(&format!(
1076 r##"#[allow(unused)]
1077 #[doc(hidden)]
1078 #[doc = " Semantic bindings for impl_match! macro"]
1079 mod _{}"##,
1080 hasher.finish()
1081 ))
1082 .unwrap(),
1083 );
1084 let mut mod_ts = TokenStream::from_str("use super::*; fn methods()").unwrap();
1085 mod_ts.extend(once(Group(Gr::new(Brace, fn_ts))));
1086 res_ts.extend(once(Group(Gr::new(Brace, mod_ts))));
1087 }
1088 }
1089 }
1090
1091 if enm_i.is_some() {
1093 let mset: HashSet<String> = HashSet::from_iter(mmap.into_keys());
1094 let enm_n = enm_i.as_ref().unwrap().to_string();
1095 for var in enm.iter() {
1096 for name in var.methods.keys() {
1097 if !mset.contains(name) {
1098 let mut free_m: Vec<String> = mset
1099 .difference(&HashSet::from_iter(var.methods.keys().cloned()))
1100 .cloned()
1101 .collect();
1102 free_m.sort();
1103 let enm_var = format!("`enum {enm_n}::{}`", var.ident.as_ref().unwrap());
1104 if free_m.is_empty() {
1105 err += &format!(
1106 "\nInvalid method `{name}` in {enm_var}:
1107`impl(-s)` contains no freely methods to implement `match{{...}}` from {enm_var}"
1108 )
1109 } else {
1110 err += &format!(
1111 "\nInvalid method name `{name}` in {enm_var} - expected{}: `{}`",
1112 if free_m.len() == 1 { "" } else { " one of" },
1113 free_m.join("`|`")
1114 )
1115 }
1116 };
1117 }
1118 }
1119 }
1120 if !err.is_empty() {
1121 eprintln!("\nErr in impl_match! macro:{err}\n");
1122 if flags.panic {
1123 panic!("Err in impl_match! macro:{err}");
1124 }
1125 }
1126
1127 res_ts
1128}
1129
1130