#![forbid(unsafe_code)]
#![deny(missing_docs)]
use std::iter::FromIterator;
use proc_macro::{Span, TokenStream};
use quote::quote;
use syn::punctuated::Punctuated;
use syn::token::{Colon, Where};
use syn::{
parse_macro_input, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Field, Fields,
FieldsNamed, FieldsUnnamed, GenericParam, Ident, Path, PathArguments, PathSegment,
PredicateType, Type, TypeImplTrait, TypeParam, TypePath, WhereClause, WherePredicate,
};
#[proc_macro_attribute]
pub fn desugar_impl(_: TokenStream, item: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(item as DeriveInput);
let mut ty_index = 1;
let ast_generics = &mut ast.generics;
let ast_data = &mut ast.data;
let mut convert_fields = |fields: &mut Punctuated<_, _>| {
for Field { ty, .. } in fields {
if let Type::ImplTrait(TypeImplTrait { bounds, .. }) = ty {
let type_ident = format!("Ty{}", ty_index);
ty_index += 1;
let type_ident = Ident::new(&type_ident, Span::call_site().into());
let type_path = Type::Path(TypePath {
qself: None,
path: Path {
leading_colon: None,
segments: Punctuated::from_iter([PathSegment {
ident: type_ident.clone(),
arguments: PathArguments::None,
}]),
},
});
let predicate = WherePredicate::Type(PredicateType {
lifetimes: None,
bounded_ty: type_path.clone(),
colon_token: Colon::default(),
bounds: bounds.clone(),
});
match &mut ast_generics.where_clause {
Some(where_clause) => {
where_clause.predicates.push(predicate);
}
where_clause @ None => {
*where_clause = Some(WhereClause {
where_token: Where::default(),
predicates: Punctuated::from_iter([predicate]),
});
}
}
ast_generics.params.push(GenericParam::Type(TypeParam {
attrs: Vec::new(),
ident: type_ident,
colon_token: None,
bounds: Default::default(),
eq_token: None,
default: None,
}));
*ty = type_path;
}
}
};
let mut convert_some_fields = |fields: &mut Fields| match fields {
Fields::Named(FieldsNamed { named: fields, .. })
| Fields::Unnamed(FieldsUnnamed {
unnamed: fields, ..
}) => {
convert_fields(fields);
}
Fields::Unit => {}
};
match ast_data {
Data::Struct(DataStruct { fields, .. }) => {
convert_some_fields(fields);
}
Data::Union(DataUnion {
fields: FieldsNamed { named: fields, .. },
..
}) => {
convert_fields(fields);
}
Data::Enum(DataEnum { variants, .. }) => {
for variant in variants {
convert_some_fields(&mut variant.fields);
}
}
}
TokenStream::from(quote! { #ast })
}