blob: 24d5fd903d67bd8b36917798513c27aa591c708d [file] [log] [blame]
use proc_macro2::TokenStream;
use quote::{format_ident, quote_spanned};
use syn::{
parse::{ParseBuffer, ParseStream},
punctuated::Punctuated,
token::{self, Comma},
*,
};
pub(crate) const DEFAULT_LIFETIME_NAME: &str = "'pin";
pub(crate) const CURRENT_PRIVATE_MODULE: &str = "__private";
pub(crate) type Variants = Punctuated<Variant, token::Comma>;
pub(crate) use Mutability::{Immutable, Mutable};
macro_rules! error {
($span:expr, $msg:expr) => {
syn::Error::new_spanned(&$span, $msg)
};
($span:expr, $($tt:tt)*) => {
error!($span, format!($($tt)*))
};
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) enum Mutability {
Mutable,
Immutable,
}
/// Creates the ident of projected type from the ident of the original type.
pub(crate) fn proj_ident(ident: &Ident, mutability: Mutability) -> Ident {
if mutability == Mutable {
format_ident!("__{}Projection", ident)
} else {
format_ident!("__{}ProjectionRef", ident)
}
}
/// Determines the lifetime names. Ensure it doesn't overlap with any existing lifetime names.
pub(crate) fn determine_lifetime_name(
lifetime_name: &mut String,
generics: &Punctuated<GenericParam, Comma>,
) {
let existing_lifetimes: Vec<String> = generics
.iter()
.filter_map(|param| {
if let GenericParam::Lifetime(LifetimeDef { lifetime, .. }) = param {
Some(lifetime.to_string())
} else {
None
}
})
.collect();
while existing_lifetimes.iter().any(|name| name.starts_with(&**lifetime_name)) {
lifetime_name.push('_');
}
}
/// Inserts a `lifetime` at position `0` of `generics.params`.
pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) {
if generics.lt_token.is_none() {
generics.lt_token = Some(token::Lt::default())
}
if generics.gt_token.is_none() {
generics.gt_token = Some(token::Gt::default())
}
generics.params.insert(
0,
GenericParam::Lifetime(LifetimeDef {
attrs: Vec::new(),
lifetime,
colon_token: None,
bounds: Punctuated::new(),
}),
);
}
/// Determines the visibility of the projected type and projection method.
pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility {
if let Visibility::Public(token) = vis {
syn::parse2(quote_spanned! { token.pub_token.span =>
pub(crate)
})
.unwrap()
} else {
vis.clone()
}
}
/// Check if `tokens` is an empty `TokenStream`.
/// This is almost equivalent to `syn::parse2::<Nothing>()`,
/// but produces a better error message and does not require ownership of `tokens`.
pub(crate) fn parse_as_empty(tokens: &TokenStream) -> Result<()> {
if tokens.is_empty() { Ok(()) } else { Err(error!(tokens, "unexpected token: {}", tokens)) }
}
pub(crate) trait SliceExt {
fn position_exact(&self, ident: &str) -> Result<Option<usize>>;
fn find(&self, ident: &str) -> Option<&Attribute>;
fn find_exact(&self, ident: &str) -> Result<Option<&Attribute>>;
}
pub(crate) trait VecExt {
fn find_remove(&mut self, ident: &str) -> Result<Option<Attribute>>;
}
impl SliceExt for [Attribute] {
fn position_exact(&self, ident: &str) -> Result<Option<usize>> {
self.iter()
.try_fold((0, None), |(i, mut prev), attr| {
if attr.path.is_ident(ident) {
if prev.is_some() {
return Err(error!(attr, "duplicate #[{}] attribute", ident));
}
parse_as_empty(&attr.tokens)?;
prev = Some(i);
}
Ok((i + 1, prev))
})
.map(|(_, pos)| pos)
}
fn find(&self, ident: &str) -> Option<&Attribute> {
self.iter().position(|attr| attr.path.is_ident(ident)).and_then(|i| self.get(i))
}
fn find_exact(&self, ident: &str) -> Result<Option<&Attribute>> {
self.position_exact(ident).map(|pos| pos.and_then(|i| self.get(i)))
}
}
impl VecExt for Vec<Attribute> {
fn find_remove(&mut self, ident: &str) -> Result<Option<Attribute>> {
self.position_exact(ident).map(|pos| pos.map(|i| self.remove(i)))
}
}
pub(crate) trait ParseBufferExt<'a> {
fn parenthesized(self) -> Result<ParseBuffer<'a>>;
}
impl<'a> ParseBufferExt<'a> for ParseStream<'a> {
fn parenthesized(self) -> Result<ParseBuffer<'a>> {
let content;
let _: token::Paren = syn::parenthesized!(content in self);
Ok(content)
}
}
impl<'a> ParseBufferExt<'a> for ParseBuffer<'a> {
fn parenthesized(self) -> Result<ParseBuffer<'a>> {
let content;
let _: token::Paren = syn::parenthesized!(content in self);
Ok(content)
}
}