ion_proc/
utils.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use 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;