| 1 | |
| 2 |
|
| 3 | use proc_macro2::{Delimiter, Group, Ident, Spacing, Span, TokenTree};
|
| 4 |
|
| 5 | fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> {
|
| 6 | let mut tokens = tokens.iter();
|
| 7 | let mut segments = Vec::new();
|
| 8 | let mut span = None;
|
| 9 | loop {
|
| 10 | match tokens.next() {
|
| 11 | None => break,
|
| 12 | Some(TokenTree::Literal(lit)) => {
|
| 13 | |
| 14 | let mut value = lit.to_string();
|
| 15 | if value.starts_with('"') && value.ends_with('"') {
|
| 16 | value.remove(0);
|
| 17 | value.pop();
|
| 18 | }
|
| 19 | segments.push((value, lit.span()));
|
| 20 | }
|
| 21 | Some(TokenTree::Ident(ident)) => {
|
| 22 | let mut value = ident.to_string();
|
| 23 | if value.starts_with("r#") {
|
| 24 | value.replace_range(0..2, "");
|
| 25 | }
|
| 26 | segments.push((value, ident.span()));
|
| 27 | }
|
| 28 | Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
|
| 29 | let Some(TokenTree::Ident(ident)) = tokens.next() else {
|
| 30 | panic!("expected identifier as modifier");
|
| 31 | };
|
| 32 |
|
| 33 | let (mut value, sp) = segments.pop().expect("expected identifier before modifier");
|
| 34 | match ident.to_string().as_str() {
|
| 35 | |
| 36 | "span" => {
|
| 37 | assert!(
|
| 38 | span.is_none(),
|
| 39 | "span modifier should only appear at most once"
|
| 40 | );
|
| 41 | span = Some(sp);
|
| 42 | }
|
| 43 | "lower" => value = value.to_lowercase(),
|
| 44 | "upper" => value = value.to_uppercase(),
|
| 45 | v => panic!("unknown modifier `{v}`"),
|
| 46 | };
|
| 47 | segments.push((value, sp));
|
| 48 | }
|
| 49 | Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::None => {
|
| 50 | let tokens = group.stream().into_iter().collect::<Vec<TokenTree>>();
|
| 51 | segments.append(&mut concat_helper(tokens.as_slice()));
|
| 52 | }
|
| 53 | token => panic!("unexpected token in paste segments: {token:?}"),
|
| 54 | };
|
| 55 | }
|
| 56 |
|
| 57 | segments
|
| 58 | }
|
| 59 |
|
| 60 | fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
|
| 61 | let segments = concat_helper(tokens);
|
| 62 | let pasted: String = segments.into_iter().map(|x| x.0).collect();
|
| 63 | TokenTree::Ident(Ident::new(&pasted, group_span))
|
| 64 | }
|
| 65 |
|
| 66 | pub(crate) fn expand(tokens: &mut Vec<TokenTree>) {
|
| 67 | for token in tokens.iter_mut() {
|
| 68 | if let TokenTree::Group(group) = token {
|
| 69 | let delimiter = group.delimiter();
|
| 70 | let span = group.span();
|
| 71 | let mut stream: Vec<_> = group.stream().into_iter().collect();
|
| 72 | |
| 73 | if delimiter == Delimiter::Bracket
|
| 74 | && stream.len() >= 3
|
| 75 | && matches!(&stream[0], TokenTree::Punct(p) if p.as_char() == '<')
|
| 76 | && matches!(&stream[stream.len() - 1], TokenTree::Punct(p) if p.as_char() == '>')
|
| 77 | {
|
| 78 | |
| 79 | *token = concat(&stream[1..stream.len() - 1], span);
|
| 80 | } else {
|
| 81 | |
| 82 | expand(&mut stream);
|
| 83 | let mut group = Group::new(delimiter, stream.into_iter().collect());
|
| 84 | group.set_span(span);
|
| 85 | *token = TokenTree::Group(group);
|
| 86 | }
|
| 87 | }
|
| 88 | }
|
| 89 |
|
| 90 | |
| 91 | for i in (0..tokens.len().saturating_sub(3)).rev() {
|
| 92 | |
| 93 | if matches!(
|
| 94 | (&tokens[i + 1], &tokens[i + 2]),
|
| 95 | (TokenTree::Punct(a), TokenTree::Punct(b))
|
| 96 | if a.as_char() == ':' && a.spacing() == Spacing::Joint && b.as_char() == ':'
|
| 97 | ) {
|
| 98 | match &tokens[i + 3] {
|
| 99 | TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
|
| 100 | tokens.splice(i + 3..i + 4, group.stream());
|
| 101 | }
|
| 102 | _ => (),
|
| 103 | }
|
| 104 |
|
| 105 | match &tokens[i] {
|
| 106 | TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
|
| 107 | tokens.splice(i..i + 1, group.stream());
|
| 108 | }
|
| 109 | _ => (),
|
| 110 | }
|
| 111 | }
|
| 112 | }
|
| 113 | }
|