ouroboros_macro/
utils.rs

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use heck::ToSnakeCase;
use proc_macro2::{Group, Ident, TokenStream, TokenTree};
use quote::{format_ident, quote};
use syn::{GenericParam, Generics, Visibility};

/// Makes phantom data definitions so that we don't get unused template parameter errors.
pub fn make_generic_consumers(generics: &Generics) -> impl Iterator<Item = (TokenStream, Ident)> {
    generics
        .params
        .clone()
        .into_iter()
        .map(|param| match param {
            GenericParam::Type(ty) => {
                let ident = &ty.ident;
                (
                    quote! { #ident },
                    format_ident!(
                        "_consume_template_type_{}",
                        ident.to_string().to_snake_case()
                    ),
                )
            }
            GenericParam::Lifetime(lt) => {
                let lifetime = &lt.lifetime;
                let ident = &lifetime.ident;
                (
                    quote! { &#lifetime () },
                    format_ident!("_consume_template_lifetime_{}", ident),
                )
            }
            // rustc don't require constants to consume, so we just skip it. 
            GenericParam::Const(ct) => {
                let ident = ct.ident;
                (
                    quote! { () },
                    format_ident!(
                        "_comsume_template_const_parameter_{}",
                        ident.to_string().to_snake_case()
                    ),
                )
            },
        })
}

// Takes the generics parameters from the original struct and turns them into arguments.
pub fn make_generic_arguments(generics: Vec<&GenericParam>) -> Vec<TokenStream> {
    let mut arguments = Vec::new();
    for generic in generics {
        match generic {
            GenericParam::Type(typ) => {
                let ident = &typ.ident;
                arguments.push(quote! { #ident });
            }
            GenericParam::Lifetime(lt) => {
                let lifetime = &lt.lifetime;
                arguments.push(quote! { #lifetime });
            }
            GenericParam::Const(ct) => {
                let ident = &ct.ident;
                arguments.push(quote! { #ident });
            },
        }
    }
    arguments
}

pub fn uses_this_lifetime(input: TokenStream) -> bool {
    for token in input.into_iter() {
        match token {
            TokenTree::Ident(ident) => {
                if ident == "this" {
                    return true;
                }
            }
            TokenTree::Group(group) => {
                if uses_this_lifetime(group.stream()) {
                    return true;
                }
            }
            _ => (),
        }
    }
    false
}

pub fn replace_this_with_lifetime(input: TokenStream, lifetime: Ident) -> TokenStream {
    input
        .into_iter()
        .map(|token| match &token {
            TokenTree::Ident(ident) => {
                if ident == "this" {
                    TokenTree::Ident(lifetime.clone())
                } else {
                    token
                }
            }
            TokenTree::Group(group) => TokenTree::Group(Group::new(
                group.delimiter(),
                replace_this_with_lifetime(group.stream(), lifetime.clone()),
            )),
            _ => token,
        })
        .collect()
}

pub fn submodule_contents_visibility(original_visibility: &Visibility) -> Visibility {
    match original_visibility {
        // inherited: allow parent of inner submodule to see
        Visibility::Inherited => syn::parse_quote! { pub(super) },
        // restricted: add an extra super if needed
        Visibility::Restricted(ref restricted) => {
            let is_first_component_super = restricted
                .path
                .segments
                .first()
                .map(|segm| segm.ident == "super")
                .unwrap_or(false);
            if restricted.path.leading_colon.is_none() && is_first_component_super {
                let mut new_visibility = restricted.clone();
                new_visibility.in_token = Some(
                    restricted
                        .in_token
                        .unwrap_or_else(|| syn::parse_quote! { in }),
                );
                new_visibility.path.segments = std::iter::once(syn::parse_quote! { super })
                    .chain(restricted.path.segments.iter().cloned())
                    .collect();
                Visibility::Restricted(new_visibility)
            } else {
                original_visibility.clone()
            }
        }
        // others are absolute, can use them as-is
        _ => original_visibility.clone(),
    }
}

/// Functionality inspired by `Inflector`, reimplemented here to avoid the
/// `regex` dependency.
pub fn to_class_case(s: &str) -> String {
    s.split('_')
        .flat_map(|word| {
            let mut chars = word.chars();
            let first = chars.next();
            // Unicode allows for a single character to become multiple characters when converting between cases.
            first
                .into_iter()
                .flat_map(|c| c.to_uppercase())
                .chain(chars.flat_map(|c| c.to_lowercase()))
        })
        .collect()
}