delegate_attr/
lib.rs

1//! Attribute proc-macro to delegate method to a field.
2//!
3//! ## Examples
4//!
5//! ### Delegate `impl` block
6//!
7//! ```
8//! use delegate_attr::delegate;
9//!
10//! struct Foo(String);
11//!
12//! #[delegate(self.0)]
13//! impl Foo {
14//!     fn as_str(&self) -> &str {}
15//!     fn into_bytes(self) -> Vec<u8> {}
16//! }
17//!
18//! let foo = Foo("hello".to_owned());
19//! assert_eq!(foo.as_str(), "hello");
20//! assert_eq!(foo.into_bytes(), b"hello");
21//! ```
22//!
23//! ### Delegate trait `impl`
24//!
25//! ```
26//! # use delegate_attr::delegate;
27//!
28//! struct Iter(std::vec::IntoIter<u8>);
29//!
30//! #[delegate(self.0)]
31//! impl Iterator for Iter {
32//!     type Item = u8;
33//!     fn next(&mut self) -> Option<u8> {}
34//!     fn count(self) -> usize {}
35//!     fn size_hint(&self) -> (usize, Option<usize>) {}
36//!     fn last(self) -> Option<u8> {}
37//! }
38//!
39//! let iter = Iter(vec![1, 2, 4, 8].into_iter());
40//! assert_eq!(iter.count(), 4);
41//! let iter = Iter(vec![1, 2, 4, 8].into_iter());
42//! assert_eq!(iter.last(), Some(8));
43//! let iter = Iter(vec![1, 2, 4, 8].into_iter());
44//! assert_eq!(iter.sum::<u8>(), 15);
45//! ```
46//!
47//! ### With more complicated target
48//!
49//! ```
50//! # use delegate_attr::delegate;
51//! # use std::cell::RefCell;
52//! struct Foo<T> {
53//!     inner: RefCell<Vec<T>>,
54//! }
55//!
56//! #[delegate(self.inner.borrow())]
57//! impl<T> Foo<T> {
58//!     fn len(&self) -> usize {}
59//! }
60//!
61//! #[delegate(self.inner.borrow_mut())]
62//! impl<T> Foo<T> {
63//!     fn push(&self, value: T) {}
64//! }
65//!
66//! #[delegate(self.inner.into_inner())]
67//! impl<T> Foo<T> {
68//!     fn into_boxed_slice(self) -> Box<[T]> {}
69//! }
70//!
71//! let foo = Foo { inner: RefCell::new(vec![1]) };
72//! assert_eq!(foo.len(), 1);
73//! foo.push(2);
74//! assert_eq!(foo.len(), 2);
75//! assert_eq!(foo.into_boxed_slice().as_ref(), &[1, 2]);
76//! ```
77//!
78//! ### `into` and `call` attribute
79//!
80//! ```
81//! # use delegate_attr::delegate;
82//! struct Inner;
83//! impl Inner {
84//!     pub fn method(&self, num: u32) -> u32 { num }
85//! }
86//!
87//! struct Wrapper { inner: Inner }
88//!
89//! #[delegate(self.inner)]
90//! impl Wrapper {
91//!     // calls method, converts result to u64
92//!     #[into]
93//!     pub fn method(&self, num: u32) -> u64 {}
94//!
95//!     // calls method, returns ()
96//!     #[call(method)]
97//!     pub fn method_noreturn(&self, num: u32) {}
98//! }
99//! ```
100//!
101//! ### Delegate single method
102//!
103//! ```
104//! # use delegate_attr::delegate;
105//! struct Foo<T>(Vec<T>);
106//!
107//! impl<T> Foo<T> {
108//!     #[delegate(self.0)]
109//!     fn len(&self) -> usize {}
110//! }
111//!
112//! let foo = Foo(vec![1]);
113//! assert_eq!(foo.len(), 1);
114//! ```
115
116extern crate proc_macro;
117
118use proc_macro::TokenStream as RawTokenStream;
119use proc_macro2::{Group, Ident, TokenStream, TokenTree};
120use quote::{quote, quote_spanned, ToTokens};
121use syn::spanned::Spanned;
122use syn::{parse_macro_input, Expr, FnArg, ImplItem, ImplItemFn, ItemImpl, Meta, Pat, ReturnType};
123
124#[proc_macro_attribute]
125pub fn delegate(attr: RawTokenStream, item: RawTokenStream) -> RawTokenStream {
126    let receiver = parse_macro_input!(attr as Expr);
127    delegate_input(item.into(), &receiver).into()
128}
129
130fn delegate_input(input: TokenStream, receiver: &Expr) -> TokenStream {
131    if let Ok(input) = syn::parse2::<ItemImpl>(input.clone()) {
132        return delegate_impl_block(input, receiver);
133    }
134    if let Ok(input) = syn::parse2::<ImplItemFn>(input.clone()) {
135        return delegate_fn(input, receiver);
136    }
137    let mut tokens = input.into_iter();
138    let first_non_attr_token = 'outer: loop {
139        match tokens.next() {
140            None => break None,
141            Some(TokenTree::Punct(p)) if p.as_char() == '#' => {}
142            Some(token) => break Some(token),
143        }
144        loop {
145            match tokens.next() {
146                None => break 'outer None,
147                Some(TokenTree::Punct(_)) => {}
148                Some(TokenTree::Group(_)) => continue 'outer,
149                Some(token) => break 'outer Some(token),
150            }
151        }
152    };
153    if let Some(token) = first_non_attr_token {
154        let msg = match &token {
155            TokenTree::Ident(ident) if ident == "impl" => "invalid impl block for #[delegate]",
156            TokenTree::Ident(ident) if ident == "fn" => "invalid method for #[delegate]",
157            _ => "expected an impl block or method inside impl block",
158        };
159        quote_spanned! { token.span() => compile_error!(#msg); }
160    } else {
161        panic!("unexpected eof")
162    }
163}
164
165fn delegate_impl_block(input: ItemImpl, receiver: &Expr) -> TokenStream {
166    let ItemImpl {
167        attrs,
168        defaultness,
169        unsafety,
170        impl_token,
171        mut generics,
172        trait_,
173        self_ty,
174        brace_token: _,
175        items,
176    } = input;
177    let where_clause = generics.where_clause.take();
178    let trait_ = trait_.map(|(bang, path, for_)| quote!(#bang #path #for_));
179    let items = items.into_iter().map(|item| {
180        let func = match item {
181            ImplItem::Fn(f) => f,
182            _ => return item.into_token_stream(),
183        };
184        delegate_fn(func, receiver)
185    });
186
187    quote! {
188        #(#attrs)* #defaultness #unsafety #impl_token #generics #trait_ #self_ty #where_clause {
189            #(#items)*
190        }
191    }
192}
193
194fn delegate_fn(input: ImplItemFn, receiver: &Expr) -> TokenStream {
195    let ImplItemFn {
196        mut attrs,
197        vis,
198        defaultness,
199        sig,
200        block: _,
201    } = input;
202    let mut errors = TokenStream::new();
203    macro_rules! push_error {
204        ($error: expr) => {
205            errors.extend($error.into_compile_error())
206        };
207        ($span: expr, $msg: expr) => {
208            push_error!(syn::Error::new($span, $msg))
209        };
210    }
211    // Parse attributes.
212    let mut has_inline = false;
213    let mut has_into = false;
214    let mut call_name = None;
215    attrs.retain(|attr| {
216        let path = attr.path();
217        if path.is_ident("inline") {
218            has_inline = true;
219        } else if path.is_ident("into") {
220            match &attr.meta {
221                Meta::List(meta) => {
222                    push_error!(meta.delimiter.span().join(), "unexpected argument")
223                }
224                Meta::NameValue(meta) => push_error!(meta.eq_token.span, "unexpected argument"),
225                Meta::Path(_) => {}
226            }
227            if has_into {
228                push_error!(attr.span(), "duplicate #[into] attribute");
229            }
230            has_into = true;
231            return false;
232        } else if path.is_ident("call") {
233            match attr.parse_args::<Ident>() {
234                Ok(ident) => {
235                    if call_name.is_some() {
236                        push_error!(attr.span(), "duplicate #[call] attribute");
237                    }
238                    call_name = Some(ident);
239                }
240                Err(e) => push_error!(e),
241            }
242            return false;
243        }
244        true
245    });
246    // Mark method always inline if it's not otherwise specified.
247    let inline = if !has_inline {
248        quote!(#[inline(always)])
249    } else {
250        quote!()
251    };
252    let mut inputs = sig.inputs.iter();
253    // Extract the self token.
254    let self_token = match inputs.next() {
255        Some(FnArg::Receiver(receiver)) => receiver.self_token.to_token_stream(),
256        Some(FnArg::Typed(pat)) => match &*pat.pat {
257            Pat::Ident(ident) if ident.ident == "self" => ident.ident.to_token_stream(),
258            _ => {
259                push_error!(pat.span(), "expected self");
260                TokenStream::new()
261            }
262        },
263        None => {
264            push_error!(sig.paren_token.span.join(), "expected self");
265            TokenStream::new()
266        }
267    };
268    // List all parameters.
269    let args = inputs
270        .filter_map(|arg| match arg {
271            FnArg::Typed(pat) => match &*pat.pat {
272                Pat::Ident(ident) => Some(ident.to_token_stream()),
273                _ => {
274                    push_error!(pat.pat.span(), "expect an identifier");
275                    None
276                }
277            },
278            _ => {
279                push_error!(arg.span(), "unexpected argument");
280                None
281            }
282        })
283        .collect::<Vec<_>>();
284    // Return errors if any.
285    if !errors.is_empty() {
286        return errors;
287    } else {
288        // Drop it to ensure that we are not pushing anymore into it.
289        drop(errors);
290    }
291    // Generate method call.
292    let name = call_name.as_ref().unwrap_or(&sig.ident);
293    // Replace the self token in the receiver with the token we extract above to ensure it comes
294    // from the right hygiene context.
295    let receiver = replace_self(receiver.to_token_stream(), &self_token);
296    let body = quote! { #receiver.#name(#(#args),*) };
297    let body = match &sig.output {
298        ReturnType::Default => quote! { #body; },
299        ReturnType::Type(_, ty) if has_into => {
300            quote! { ::std::convert::Into::<#ty>::into(#body) }
301        }
302        _ => body,
303    };
304    quote! {
305        #(#attrs)* #inline #vis #defaultness #sig {
306            #body
307        }
308    }
309}
310
311fn replace_self(expr: TokenStream, self_token: &TokenStream) -> TokenStream {
312    expr.into_iter()
313        .map(|token| match token {
314            TokenTree::Ident(ident) if ident == "self" => self_token.clone(),
315            TokenTree::Group(group) => {
316                let delimiter = group.delimiter();
317                let stream = replace_self(group.stream(), self_token);
318                Group::new(delimiter, stream).into_token_stream()
319            }
320            _ => token.into_token_stream(),
321        })
322        .collect()
323}