use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
Attribute, Ident, Token, Type, TypeParamBound, TypePath, parse::ParseStream, punctuated::Punctuated,
visit_mut::VisitMut,
};
use crate::util::attribute_tokens;
#[derive(Debug, Clone)]
pub struct AssocType {
pub attrs: Vec<Attribute>,
pub ident: Ident,
pub bounds: Punctuated<TypeParamBound, Token![+]>,
}
impl AssocType {
pub fn lifted_ident(&self) -> Ident {
format_ident!("__{}", &self.ident)
}
pub fn trait_decl(&self) -> TokenStream {
let attrs = attribute_tokens(&self.attrs);
let ident = &self.ident;
let bounds = &self.bounds;
if bounds.is_empty() {
quote! { #attrs type #ident; }
} else {
quote! { #attrs type #ident: #bounds; }
}
}
pub fn parse_with_attrs(input: ParseStream, attrs: Vec<Attribute>) -> syn::Result<Self> {
input.parse::<Token![type]>()?;
let ident: Ident = input.parse()?;
if input.peek(Token![<]) {
return Err(input.error("generic associated types are not supported in remote traits"));
}
let mut bounds: Punctuated<TypeParamBound, Token![+]> = Punctuated::new();
if input.peek(Token![:]) {
input.parse::<Token![:]>()?;
loop {
bounds.push_value(input.parse()?);
if !input.peek(Token![+]) {
break;
}
bounds.push_punct(input.parse()?);
}
}
if input.peek(Token![=]) {
return Err(input.error("associated type defaults are not supported in remote traits"));
}
input.parse::<Token![;]>()?;
Ok(Self { attrs, ident, bounds })
}
}
pub fn remove_self_type(ty: &Type, assoc: &[AssocType]) -> Type {
struct SelfAssocRewriter<'a> {
pub assoc: &'a [AssocType],
}
impl<'a> VisitMut for SelfAssocRewriter<'a> {
fn visit_type_mut(&mut self, t: &mut Type) {
if let Type::Path(tp) = t {
if let Some(qs) = &tp.qself
&& let Type::Path(self_tp) = &*qs.ty
&& self_tp.path.is_ident("Self")
&& let Some(last) = tp.path.segments.last()
&& let Some(a) = self.assoc.iter().find(|a| a.ident == last.ident)
{
let lifted = a.lifted_ident();
*t = Type::Path(TypePath { attrs: tp.attrs.clone(), qself: None, path: lifted.into() });
return;
}
if tp.qself.is_none() {
let segs = &tp.path.segments;
if segs.len() == 2
&& segs[0].ident == "Self"
&& let Some(a) = self.assoc.iter().find(|a| a.ident == segs[1].ident)
{
let lifted = a.lifted_ident();
*t = Type::Path(TypePath { attrs: tp.attrs.clone(), qself: None, path: lifted.into() });
return;
}
}
}
syn::visit_mut::visit_type_mut(self, t);
}
}
let mut ty = ty.clone();
SelfAssocRewriter { assoc }.visit_type_mut(&mut ty);
ty
}