aboutsummaryrefslogtreecommitdiff
path: root/macros/src/util/tokens.rs
blob: c614ea0eef36b366f379c6e03242f6ad5103d5e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use std::fmt::Write;

use proc_macro2::{Delimiter, Spacing, TokenTree};
use quote::ToTokens;

pub trait ToTokensExt
{
    fn to_str_pretty(&self) -> String;
}

impl<T: ToTokens> ToTokensExt for T
{
    fn to_str_pretty(&self) -> String
    {
        let mut spaceable = Spaceable::None;

        self.to_token_stream()
            .into_iter()
            .fold(String::new(), |mut acc, token_tree| {
                let prev_spaceable = spaceable;

                spaceable = get_tt_spaceable(&token_tree);

                if matches!(prev_spaceable, Spaceable::Left | Spaceable::LeftRight)
                    && matches!(spaceable, Spaceable::Right | Spaceable::LeftRight)
                {
                    write!(acc, " ").ok();
                }

                match token_tree {
                    TokenTree::Group(group) => match group.delimiter() {
                        Delimiter::Parenthesis => {
                            write!(acc, "({})", group.stream().to_str_pretty()).ok();
                        }
                        Delimiter::Brace => {
                            write!(acc, "{{{}}}", group.stream().to_str_pretty()).ok();
                        }
                        Delimiter::Bracket => {
                            write!(acc, "[{}]", group.stream().to_str_pretty()).ok();
                        }
                        Delimiter::None => {
                            write!(acc, "{}", group.stream().to_str_pretty()).ok();
                        }
                    },
                    tt => {
                        write!(acc, "{tt}").ok();
                    }
                }

                acc
            })
    }
}

fn get_tt_spaceable(token_tree: &TokenTree) -> Spaceable
{
    match &token_tree {
        TokenTree::Ident(_) => Spaceable::LeftRight,
        TokenTree::Punct(punct)
            if punct.spacing() == Spacing::Alone && (punct.as_char() == '+') =>
        {
            Spaceable::LeftRight
        }
        TokenTree::Punct(punct)
            if punct.spacing() == Spacing::Alone
                && (punct.as_char() == '>' || punct.as_char() == ',') =>
        {
            Spaceable::Left
        }
        TokenTree::Punct(punct)
            if punct.spacing() == Spacing::Joint && punct.as_char() == '-' =>
        {
            // Is part of ->
            Spaceable::Right
        }
        TokenTree::Punct(punct) if punct.as_char() == '&' => Spaceable::Right,
        TokenTree::Group(_) => Spaceable::Left,
        _ => Spaceable::None,
    }
}

#[derive(Debug, Clone, Copy)]
enum Spaceable
{
    Left,
    Right,
    LeftRight,
    None,
}