#![doc(html_root_url = "https://docs.rs/easy-ext/0.1.3")]
#![deny(unsafe_code)]
#![deny(rust_2018_idioms)]
#![deny(unreachable_pub)]
extern crate proc_macro;
use std::mem;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{
parse_macro_input, parse_quote, Attribute, Ident, ImplItem, ImplItemConst, ImplItemMethod,
ImplItemType, ItemImpl, ItemTrait, TraitItem, TraitItemConst, TraitItemMethod, TraitItemType,
Visibility,
};
#[proc_macro_attribute]
pub fn ext(args: TokenStream, input: TokenStream) -> TokenStream {
let mut input_impl: ItemImpl = parse_macro_input!(input);
let ext_ident: Ident = parse_macro_input!(args);
let mut tts = quote!(#[allow(patterns_in_fns_without_body)]); tts.extend(trait_from_item(&mut input_impl, ext_ident).into_token_stream());
tts.extend(input_impl.into_token_stream());
TokenStream::from(tts)
}
fn trait_from_item(item_impl: &mut ItemImpl, ident: Ident) -> ItemTrait {
let generics = item_impl.generics.clone();
let ty_generics = generics.split_for_impl().1;
let trait_ = parse_quote!(#ident #ty_generics);
item_impl.trait_ = Some((None, trait_, default()));
let mut vis = None;
let mut items = Vec::with_capacity(item_impl.items.len());
item_impl.items.iter_mut().for_each(|item| {
items.push(trait_item_from_impl_item(item, |_vis| match &vis {
Some(v) if *v == _vis => {}
Some(_) => panic!("visibility mismatch"),
None => vis = Some(_vis),
}))
});
ItemTrait {
attrs: item_impl.attrs.clone(),
vis: vis.unwrap_or(Visibility::Inherited),
unsafety: item_impl.unsafety,
auto_token: None,
trait_token: default(),
ident,
generics,
colon_token: None,
supertraits: default(),
brace_token: default(),
items,
}
}
fn trait_item_from_impl_item(impl_item: &mut ImplItem, f: impl FnOnce(Visibility)) -> TraitItem {
macro_rules! from {
($($v:ident => $i:ident,)*) => {
match impl_item {
$(ImplItem::$v(item) => {
let vis = mem::replace(&mut item.vis, Visibility::Inherited);
item.defaultness = None;
f(vis);
TraitItem::$v($i(item))
})*
_ => panic!("unsupported item"),
}
};
}
from! {
Const => const_from_const,
Method => method_from_method,
Type => type_from_type,
}
}
fn type_from_type(impl_type: &ImplItemType) -> TraitItemType {
TraitItemType {
attrs: impl_type.attrs.clone(),
type_token: default(),
ident: impl_type.ident.clone(),
generics: impl_type.generics.clone(),
colon_token: None,
bounds: default(),
default: None,
semi_token: default(),
}
}
fn const_from_const(impl_const: &ImplItemConst) -> TraitItemConst {
TraitItemConst {
attrs: impl_const.attrs.clone(),
const_token: default(),
ident: impl_const.ident.clone(),
colon_token: default(),
ty: impl_const.ty.clone(),
default: None,
semi_token: default(),
}
}
fn method_from_method(impl_method: &ImplItemMethod) -> TraitItemMethod {
let mut attrs = impl_method.attrs.clone();
find_remove(&mut attrs, "inline");
TraitItemMethod {
attrs,
sig: impl_method.sig.clone(),
default: None,
semi_token: Some(default()),
}
}
fn default<T: Default>() -> T {
T::default()
}
fn find_remove(attrs: &mut Vec<Attribute>, ident: &str) -> Option<Attribute> {
attrs
.iter()
.position(|Attribute { path, .. }| path.is_ident(ident))
.map(|i| attrs.remove(i))
}