pgx_sql_entity_graph/
lifetimes.rs

1use crate::NameMacro;
2use proc_macro2::TokenStream;
3
4pub fn staticize_lifetimes_in_type_path(value: syn::TypePath) -> syn::TypePath {
5    let mut ty = syn::Type::Path(value);
6    staticize_lifetimes(&mut ty);
7    match ty {
8        syn::Type::Path(type_path) => type_path,
9
10        // shouldn't happen
11        _ => panic!("not a TypePath"),
12    }
13}
14
15pub fn staticize_lifetimes(value: &mut syn::Type) {
16    match value {
17        syn::Type::Path(type_path) => {
18            for segment in &mut type_path.path.segments {
19                match &mut segment.arguments {
20                    syn::PathArguments::AngleBracketed(bracketed) => {
21                        for arg in &mut bracketed.args {
22                            match arg {
23                                // rename lifetimes to the static lifetime so the TypeIds match.
24                                syn::GenericArgument::Lifetime(lifetime) => {
25                                    lifetime.ident =
26                                        syn::Ident::new("static", lifetime.ident.span());
27                                }
28
29                                // recurse
30                                syn::GenericArgument::Type(ty) => staticize_lifetimes(ty),
31                                syn::GenericArgument::Binding(binding) => {
32                                    staticize_lifetimes(&mut binding.ty)
33                                }
34                                syn::GenericArgument::Constraint(constraint) => {
35                                    for bound in constraint.bounds.iter_mut() {
36                                        match bound {
37                                            syn::TypeParamBound::Lifetime(lifetime) => {
38                                                lifetime.ident =
39                                                    syn::Ident::new("static", lifetime.ident.span())
40                                            }
41                                            _ => {}
42                                        }
43                                    }
44                                }
45
46                                // nothing to do otherwise
47                                _ => {}
48                            }
49                        }
50                    }
51                    _ => {}
52                }
53            }
54        }
55
56        syn::Type::Reference(type_ref) => match &mut type_ref.lifetime {
57            Some(ref mut lifetime) => {
58                lifetime.ident = syn::Ident::new("static", lifetime.ident.span());
59            }
60            this @ None => *this = Some(syn::parse_quote!('static)),
61        },
62
63        syn::Type::Tuple(type_tuple) => {
64            for elem in &mut type_tuple.elems {
65                staticize_lifetimes(elem);
66            }
67        }
68
69        syn::Type::Macro(type_macro) => {
70            let mac = &type_macro.mac;
71            if let Some(archetype) = mac.path.segments.last() {
72                match archetype.ident.to_string().as_str() {
73                    "name" => {
74                        if let Ok(out) = mac.parse_body::<NameMacro>() {
75                            // We don't particularly care what the identifier is, so we parse a
76                            // raw TokenStream.  Specifically, it's okay for the identifier String,
77                            // which we end up using as a Postgres column name, to be nearly any
78                            // string, which can include Rust reserved words such as "type" or "match"
79                            if let Ok(ident) = syn::parse_str::<TokenStream>(&out.ident) {
80                                let mut ty = out.used_ty.resolved_ty;
81
82                                // rewrite the name!() macro's type so that it has a static lifetime, if any
83                                staticize_lifetimes(&mut ty);
84                                type_macro.mac = syn::parse_quote! {::pgx::name!(#ident, #ty)};
85                            }
86                        }
87                    }
88                    _ => {}
89                }
90            }
91        }
92        _ => {}
93    }
94}
95
96pub fn anonymize_lifetimes_in_type_path(value: syn::TypePath) -> syn::TypePath {
97    let mut ty = syn::Type::Path(value);
98    anonymize_lifetimes(&mut ty);
99    match ty {
100        syn::Type::Path(type_path) => type_path,
101
102        // shouldn't happen
103        _ => panic!("not a TypePath"),
104    }
105}
106
107pub fn anonymize_lifetimes(value: &mut syn::Type) {
108    match value {
109        syn::Type::Path(type_path) => {
110            for segment in &mut type_path.path.segments {
111                match &mut segment.arguments {
112                    syn::PathArguments::AngleBracketed(bracketed) => {
113                        for arg in &mut bracketed.args {
114                            match arg {
115                                // rename lifetimes to the anonymous lifetime
116                                syn::GenericArgument::Lifetime(lifetime) => {
117                                    lifetime.ident = syn::Ident::new("_", lifetime.ident.span());
118                                }
119
120                                // recurse
121                                syn::GenericArgument::Type(ty) => anonymize_lifetimes(ty),
122                                syn::GenericArgument::Binding(binding) => {
123                                    anonymize_lifetimes(&mut binding.ty)
124                                }
125                                syn::GenericArgument::Constraint(constraint) => {
126                                    for bound in constraint.bounds.iter_mut() {
127                                        match bound {
128                                            syn::TypeParamBound::Lifetime(lifetime) => {
129                                                lifetime.ident =
130                                                    syn::Ident::new("_", lifetime.ident.span())
131                                            }
132                                            _ => {}
133                                        }
134                                    }
135                                }
136
137                                // nothing to do otherwise
138                                _ => {}
139                            }
140                        }
141                    }
142                    _ => {}
143                }
144            }
145        }
146
147        syn::Type::Reference(type_ref) => {
148            if let Some(lifetime) = type_ref.lifetime.as_mut() {
149                lifetime.ident = syn::Ident::new("_", lifetime.ident.span());
150            }
151        }
152
153        syn::Type::Tuple(type_tuple) => {
154            for elem in &mut type_tuple.elems {
155                anonymize_lifetimes(elem);
156            }
157        }
158
159        _ => {}
160    }
161}