-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib.rs
122 lines (100 loc) · 3.66 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#![feature(proc_macro)]
#![feature(label_break_value)]
#![recursion_limit="128"]
#[macro_use]
extern crate quote;
use proc_macro;
use proc_macro2::{TokenStream, TokenTree, Ident, Span};
use syn::{ItemStruct, punctuated::Punctuated};
use matches2::{unwrap_match, assert_matches};
/// The main macro, check out the README for more information.
#[proc_macro_attribute]
pub fn abstract_struct(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let wrap_method_name: Option<Ident> = 'wrap_method_name: {
let args: TokenStream = args.into();
let mut iter = args.into_iter();
let action = match iter.next() {
Some(TokenTree::Ident(i)) => i.to_string(),
None => break 'wrap_method_name Some(Ident::new("wrap", Span::call_site())),
_ => panic!("Expected an identifier first inside the parentheses!")
};
match action.as_ref() {
"nowrap" => {
assert_matches!(iter.next(), None, "There should be nothing after `nowrap`!");
None
},
"wrap" => {
assert_matches!(iter.next(), Some(TokenTree::Punct(ref p)) if p.as_char() == '=',
"Expected a '=' after `wrap`!");
Some(unwrap_match!(iter.next(), Some(TokenTree::Ident(i)) => i,
"Expected an identifier that should be the name of the wrap method"))
},
_ => panic!("Invalid argument!")
}
};
let input: ItemStruct = syn::parse(input).unwrap();
let vis = &input.vis;
let ident = &input.ident;
let trait_ident = Ident::new(&format!("{}Abstract", ident), ident.span());
let lifetimes: TokenStream = input.generics.lifetimes().flat_map(|l| quote!(#l,)).collect();
let lifetime_arguments: TokenStream = input.generics.lifetimes().flat_map(|l| {
let mut l = l.clone();
l.colon_token = None;
l.bounds = Punctuated::new();
quote!(#l,)
}).collect();
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
assert!(where_clause.is_none(), "abstract_struct sadly does not support where clauses");
let assoc_ty_decl: TokenStream = input.generics.type_params().flat_map(|p| {
let mut p = p.clone();
p.eq_token = None;
p.default = None;
quote!(type #p;)
}).collect();
let assoc_ty_args: TokenStream = input.generics.type_params().flat_map(|p| {
let p_ident = &p.ident;
quote!(<Self as #trait_ident<#lifetime_arguments>>::#p_ident,)
}).collect();
let assoc_ty_impl: TokenStream = input.generics.type_params().flat_map(|p| {
let p_ident = &p.ident;
quote!(type #p_ident = #p_ident;)
}).collect();
let expanded = quote! {
#input
impl #impl_generics From<abstract_struct::Wrapper<#ident #ty_generics>> for #ident #ty_generics {
fn from(w: abstract_struct::Wrapper<#ident #ty_generics>) -> #ident #ty_generics {
w.0
}
}
};
let expanded = if let Some(wrap_method_name) = wrap_method_name {
quote! {
#expanded
#[allow(dead_code)]
impl #impl_generics #ident #ty_generics {
fn #wrap_method_name(self) -> abstract_struct::Wrapper<Self> {
abstract_struct::Wrapper(self)
}
}
}
} else {
expanded
};
let expanded = quote! {
#expanded
#vis trait #trait_ident<#lifetimes> : std::ops::Deref<Target = #ident<#lifetime_arguments #assoc_ty_args>> + std::convert::Into<#ident<#lifetime_arguments #assoc_ty_args>> {
#assoc_ty_decl
}
impl #impl_generics #trait_ident<#lifetime_arguments> for abstract_struct::Wrapper<#ident #ty_generics> {
#assoc_ty_impl
}
};
expanded.into()
}
/// This macro prints the result to stdout before giving it to rustc
#[proc_macro_attribute]
pub fn abstract_struct_debug(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let output = abstract_struct(args, input);
println!("result: {}", output);
output
}