1use prettyplease::unparse;
8use proc_macro2::{Ident, TokenStream};
9use quote::quote;
10use syn::punctuated::Punctuated;
11use syn::spanned::Spanned as _;
12use syn::{
13 Attribute, Error, Fields, GenericParam, Generics, Lifetime, LifetimeParam, Meta, Pat, Path, Result, Token, Type,
14 TypeParamBound, parse_quote, parse2,
15};
16
17pub(crate) fn path_ends_with<I: ?Sized>(path: &Path, ident: &I) -> bool
18where
19 Ident: PartialEq<I>,
20{
21 if let Some(last) = path.segments.last() {
22 &last.ident == ident
23 } else {
24 false
25 }
26}
27
28pub(crate) fn pat_is_ident<I: ?Sized>(pat: &Pat, ident: &I) -> bool
29where
30 Ident: PartialEq<I>,
31{
32 if let Pat::Ident(pat) = pat {
33 &pat.ident == ident
34 } else {
35 false
36 }
37}
38
39pub(crate) fn add_trait_bounds(generics: &mut Generics, bound: &TypeParamBound) {
40 for param in &mut generics.params {
41 if let GenericParam::Type(type_param) = param {
42 type_param.bounds.push(bound.clone());
43 }
44 }
45}
46
47pub(crate) fn add_lifetime_generic(impl_generics: &mut Generics, lifetime: Lifetime) {
48 let has_cx = impl_generics.params.iter().any(|param| {
49 if let GenericParam::Lifetime(lt) = param {
50 lt.lifetime == lifetime
51 } else {
52 false
53 }
54 });
55
56 if !has_cx {
57 let param = GenericParam::Lifetime(LifetimeParam {
58 attrs: Vec::new(),
59 lifetime,
60 colon_token: None,
61 bounds: Punctuated::new(),
62 });
63
64 impl_generics.params.push(param);
65 }
66}
67
68pub(crate) fn find_repr(attrs: &[Attribute]) -> Result<Option<Ident>> {
69 let mut repr = None;
70 for attr in attrs {
71 if attr.path().is_ident("repr") {
72 let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
73 let allowed_reprs: [Ident; 8] = [
74 parse_quote!(i8),
75 parse_quote!(i16),
76 parse_quote!(i32),
77 parse_quote!(i64),
78 parse_quote!(u8),
79 parse_quote!(u16),
80 parse_quote!(u32),
81 parse_quote!(u64),
82 ];
83
84 for meta in nested {
85 if let Meta::Path(path) = &meta {
86 for allowed_repr in &allowed_reprs {
87 if path.is_ident(allowed_repr) {
88 if repr.is_none() {
89 repr = Some(path.get_ident().unwrap().clone());
90 } else {
91 return Err(Error::new(meta.span(), "Only One Representation Allowed in #[repr]"));
92 }
93 }
94 }
95 }
96 }
97 }
98 }
99
100 Ok(repr)
101}
102
103pub(crate) fn wrap_in_fields_group<I>(idents: I, fields: &Fields) -> TokenStream
104where
105 I: IntoIterator<Item = Ident>,
106{
107 let idents = idents.into_iter();
108 match fields {
109 Fields::Named(_) => quote!({ #(#idents,)* }),
110 Fields::Unnamed(_) => quote!((#(#idents,)*)),
111 Fields::Unit => quote!(),
112 }
113}
114
115pub(crate) fn format_type(ty: &Type) -> String {
116 let ty = unparse(
117 &parse2(quote!(
118 impl #ty {}
119 ))
120 .unwrap(),
121 );
122 let mut ty = String::from(ty.trim());
123 ty.drain((ty.len() - 2)..ty.len());
124 ty.drain(0..4);
125 String::from(ty.trim())
126}
127
128macro_rules! new_token {
129 [$i:tt] => {
130 <::syn::Token![$i]>::default()
131 };
132}
133
134pub(crate) use new_token;