Skip to content

Commit 53a2e89

Browse files
authored
Merge pull request #4 from KodrAus/feat/auto-ref
Support auto_impl on borrowed references
2 parents 879d14a + 4d25e04 commit 53a2e89

File tree

6 files changed

+242
-33
lines changed

6 files changed

+242
-33
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "auto_impl"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
authors = ["Ashley Mannix <[email protected]>"]
55
license = "MIT"
66
description = "Automatically implement traits for common smart pointers and closures"
@@ -18,4 +18,4 @@ proc-macro = true
1818

1919
[dependencies]
2020
quote = "~0.3"
21-
syn = { version = "~0.11", features = ["full"] }
21+
syn = { version = "~0.11", features = ["full"] }

README.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ The following types are supported:
3030
- [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html)
3131
- [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html)
3232
- [`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html)
33+
- `&`
34+
- `&mut`
3335

3436
## Implement a trait for a smart pointer
3537

@@ -108,16 +110,16 @@ There are a few restrictions on `#[auto_impl]` for smart pointers. The trait mus
108110

109111
```rust
110112
#[auto_impl(Fn)]
111-
trait FnTrait2<'a, T> {
113+
trait MyTrait<'a, T> {
112114
fn execute<'b>(&'a self, arg1: &'b T, arg2: &'static str) -> Result<(), String>;
113115
}
114116
```
115117

116118
Will expand to:
117119

118120
```rust
119-
impl<'a, T, TFn> FnTrait2<'a, T> for TFn
120-
where TFn: Fn(&T, &'static str) -> Result<(), String>
121+
impl<'a, T, TAutoImpl> MyTrait<'a, T> for TAutoImpl
122+
where TAutoImpl: Fn(&T, &'static str) -> Result<(), String>
121123
{
122124
fn execute<'b>(&'a self, arg1: &'b T, arg1: &'static str) -> Result<(), String> {
123125
self(arg1, arg2)
@@ -130,3 +132,34 @@ There are a few restrictions on `#[auto_impl]` for closures. The trait must:
130132
- Have a single method
131133
- Have no associated types
132134
- Have no non-static lifetimes in the return type
135+
136+
## Implement a trait for a borrowed reference
137+
138+
```rust
139+
#[auto_impl(&, &mut)]
140+
trait MyTrait<'a, T> {
141+
fn execute<'b>(&'a self, arg1: &'b T, arg2: &'static str) -> Result<(), String>;
142+
}
143+
```
144+
145+
Will expand to:
146+
147+
```rust
148+
impl<'auto, 'a, T, TAutoImpl> MyTrait<'a, T> for &'auto TAutoImpl {
149+
fn execute<'b>(&'a self, arg1: &'b T, arg1: &'static str) -> Result<(), String> {
150+
(**self).execute(arg1, arg2)
151+
}
152+
}
153+
154+
impl<'auto, 'a, T, TAutoImpl> MyTrait<'a, T> for &'auto mut TAutoImpl {
155+
fn execute<'b>(&'a self, arg1: &'b T, arg1: &'static str) -> Result<(), String> {
156+
(**self).execute(arg1, arg2)
157+
}
158+
}
159+
```
160+
161+
There are a few restrictions on `#[auto_impl]` for immutably borrowed references. The trait must:
162+
163+
- Only have methods that take `&self`
164+
165+
There are no restrictions on `#[auto_impl]` for mutably borrowed references.

compile_test/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ trait FnTrait3 {
1919
fn execute(&mut self);
2020
}
2121

22-
#[auto_impl(Arc, Box, Rc)]
22+
#[auto_impl(Arc, Box, Rc, &, &mut)]
2323
trait RefTrait1<'a, T: for<'b> Into<&'b str>> {
2424
type Type1;
2525
type Type2;

src/impl_as_ref.rs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ use model::*;
88
///
99
/// - The smart pointer wraps a single generic value, like `Arc<T>`, `Box<T>`, `Rc<T>`
1010
/// - The smart pointer implements `AsRef<T>`
11-
pub fn build(component: &AutoImpl, ref_ty: Trait) -> Result<Tokens, String> {
12-
let component_ident = &component.ident;
13-
11+
pub fn build_wrapper(component: &AutoImpl, ref_ty: Trait) -> Result<Tokens, String> {
1412
let impl_methods = component.methods.iter()
1513
.map(|method| {
1614
let valid_receiver = match method.arg_self {
@@ -36,6 +34,64 @@ pub fn build(component: &AutoImpl, ref_ty: Trait) -> Result<Tokens, String> {
3634
})
3735
.collect::<Result<Vec<_>, _>>()?;
3836

37+
build(component, vec![], quote!(#ref_ty < TAutoImpl >), impl_methods)
38+
}
39+
40+
/// Auto implement a trait for an immutable reference.
41+
///
42+
/// This expects the input to have the following properties:
43+
///
44+
/// - All methods have an `&self` receiver
45+
pub fn build_immutable(component: &AutoImpl) -> Result<Tokens, String> {
46+
let impl_methods = component.methods.iter()
47+
.map(|method| {
48+
let valid_receiver = match method.arg_self {
49+
Some(ref arg_self) => match *arg_self {
50+
SelfArg::Ref(_, syn::Mutability::Immutable) => true,
51+
_ => false
52+
},
53+
None => false
54+
};
55+
56+
if !valid_receiver {
57+
Err("auto impl for `&T` is only supported for methods with a `&self` reciever")?
58+
}
59+
60+
method.build_impl_item(|method| {
61+
let fn_ident = &method.ident;
62+
let fn_args = &method.arg_pats;
63+
64+
quote!({
65+
(**self).#fn_ident( #(#fn_args),* )
66+
})
67+
})
68+
})
69+
.collect::<Result<Vec<_>, _>>()?;
70+
71+
build(component, vec![quote!('auto)], quote!(&'auto TAutoImpl), impl_methods)
72+
}
73+
74+
/// Auto implement a trait for a mutable reference.
75+
pub fn build_mutable(component: &AutoImpl) -> Result<Tokens, String> {
76+
let impl_methods = component.methods.iter()
77+
.map(|method| {
78+
method.build_impl_item(|method| {
79+
let fn_ident = &method.ident;
80+
let fn_args = &method.arg_pats;
81+
82+
quote!({
83+
(**self).#fn_ident( #(#fn_args),* )
84+
})
85+
})
86+
})
87+
.collect::<Result<Vec<_>, _>>()?;
88+
89+
build(component, vec![quote!('auto)], quote!(&'auto mut TAutoImpl), impl_methods)
90+
}
91+
92+
fn build(component: &AutoImpl, extra_lifetimes: Vec<Tokens>, impl_ident: Tokens, impl_methods: Vec<syn::TraitItem>) -> Result<Tokens, String> {
93+
let component_ident = &component.ident;
94+
3995
let impl_associated_types = component.associated_types.iter()
4096
.map(|associated_type| {
4197
associated_type.build_impl_item(|associated_type| {
@@ -49,7 +105,7 @@ pub fn build(component: &AutoImpl, ref_ty: Trait) -> Result<Tokens, String> {
49105
let (trait_tys, impl_lifetimes, impl_tys, where_clauses) = component.split_generics();
50106

51107
Ok(quote!(
52-
impl< #(#impl_lifetimes,)* #(#impl_tys,)* TAutoImpl > #component_ident #trait_tys for #ref_ty < TAutoImpl >
108+
impl< #(#extra_lifetimes,)* #(#impl_lifetimes,)* #(#impl_tys,)* TAutoImpl > #component_ident #trait_tys for #impl_ident
53109
where TAutoImpl: #component_ident #trait_tys
54110
#(,#where_clauses)*
55111
{

src/lib.rs

Lines changed: 90 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ include!("lib.proc_macro.rs");
88
extern crate quote;
99
extern crate syn;
1010

11+
mod parse;
1112
mod model;
1213
mod impl_as_ref;
1314
mod impl_fn;
@@ -16,7 +17,7 @@ use std::str::FromStr;
1617
use quote::Tokens;
1718
use model::*;
1819

19-
const IMPL_FOR_TRAIT_ERR: &'static str = "expected a list containing any of `Arc`, `Rc`, `Box`, `Fn`, `FnMut` or `FnOnce`";
20+
const IMPL_FOR_TRAIT_ERR: &'static str = "expected a list containing any of `&`, `&mut`, `Arc`, `Rc`, `Box`, `Fn`, `FnMut` or `FnOnce`";
2021

2122
#[derive(Debug, PartialEq)]
2223
enum ImplForTrait {
@@ -26,6 +27,8 @@ enum ImplForTrait {
2627
Fn,
2728
FnMut,
2829
FnOnce,
30+
Ref,
31+
RefMut,
2932
}
3033

3134
impl FromStr for ImplForTrait {
@@ -39,29 +42,21 @@ impl FromStr for ImplForTrait {
3942
"Fn" => Ok(ImplForTrait::Fn),
4043
"FnMut" => Ok(ImplForTrait::FnMut),
4144
"FnOnce" => Ok(ImplForTrait::FnOnce),
42-
_ => Err(IMPL_FOR_TRAIT_ERR)?
45+
"&" => Ok(ImplForTrait::Ref),
46+
"&mut" => Ok(ImplForTrait::RefMut),
47+
c => {
48+
println!("got: {}", c);
49+
Err(IMPL_FOR_TRAIT_ERR)?
50+
}
4351
}
4452
}
4553
}
4654

4755
fn parse_impl_types(tokens: Tokens) -> Result<Vec<ImplForTrait>, String> {
48-
let attr = syn::parse_outer_attr(tokens.as_ref())?;
49-
50-
let idents: Vec<Result<ImplForTrait, String>> = match attr.value {
51-
syn::MetaItem::Word(ident) => vec![ImplForTrait::from_str(ident.as_ref())],
52-
syn::MetaItem::List(_, idents) => {
53-
idents.into_iter().map(|ident| {
54-
match ident {
55-
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ident)) => ImplForTrait::from_str(ident.as_ref()),
56-
_ => Err(IMPL_FOR_TRAIT_ERR)?
57-
}
58-
})
59-
.collect()
60-
},
61-
_ => Err(IMPL_FOR_TRAIT_ERR)?
62-
};
63-
64-
idents.into_iter().collect()
56+
parse::attr(tokens.as_str())?
57+
.into_iter()
58+
.map(|ident| ImplForTrait::from_str(&ident))
59+
.collect()
6560
}
6661

6762
fn auto_impl_expand(impl_for_traits: &[ImplForTrait], tokens: Tokens) -> Result<Tokens, String> {
@@ -71,9 +66,11 @@ fn auto_impl_expand(impl_for_traits: &[ImplForTrait], tokens: Tokens) -> Result<
7166
let impls: Vec<_> = impl_for_traits.iter()
7267
.map(|impl_for_trait| {
7368
match *impl_for_trait {
74-
ImplForTrait::Arc => impl_as_ref::build(&auto_impl, Trait::new("Arc", quote!(::std::sync::Arc))),
75-
ImplForTrait::Rc => impl_as_ref::build(&auto_impl, Trait::new("Rc", quote!(::std::rc::Rc))),
76-
ImplForTrait::Box => impl_as_ref::build(&auto_impl, Trait::new("Box", quote!(Box))),
69+
ImplForTrait::Arc => impl_as_ref::build_wrapper(&auto_impl, Trait::new("Arc", quote!(::std::sync::Arc))),
70+
ImplForTrait::Rc => impl_as_ref::build_wrapper(&auto_impl, Trait::new("Rc", quote!(::std::rc::Rc))),
71+
ImplForTrait::Box => impl_as_ref::build_wrapper(&auto_impl, Trait::new("Box", quote!(Box))),
72+
ImplForTrait::Ref => impl_as_ref::build_immutable(&auto_impl),
73+
ImplForTrait::RefMut => impl_as_ref::build_mutable(&auto_impl),
7774
ImplForTrait::Fn => impl_fn::build(&auto_impl, Trait::new("Fn", quote!(Fn))),
7875
ImplForTrait::FnMut => impl_fn::build(&auto_impl, Trait::new("FnMut", quote!(FnMut))),
7976
ImplForTrait::FnOnce => impl_fn::build(&auto_impl, Trait::new("FnOnce", quote!(FnOnce)))
@@ -137,7 +134,34 @@ mod tests {
137134

138135
let impls = parse_impl_types(input).unwrap_err();
139136

140-
assert_eq!("expected a list containing any of `Arc`, `Rc`, `Box`, `Fn`, `FnMut` or `FnOnce`", &impls);
137+
assert_eq!("expected a list containing any of `&`, `&mut`, `Arc`, `Rc`, `Box`, `Fn`, `FnMut` or `FnOnce`", &impls);
138+
}
139+
140+
#[test]
141+
fn parse_attr_single() {
142+
let input = quote!(#[auto_impl(&)]);
143+
let impl_types = parse_impl_types(input).unwrap();
144+
145+
let expected = vec![ImplForTrait::Ref];
146+
147+
assert_eq!(impl_types, expected);
148+
}
149+
150+
#[test]
151+
fn parse_attr_multi() {
152+
let input = quote!(#[auto_impl(&, &mut, Arc)]);
153+
154+
println!("{}", input);
155+
156+
let impl_types = parse_impl_types(input).unwrap();
157+
158+
let expected = vec![
159+
ImplForTrait::Ref,
160+
ImplForTrait::RefMut,
161+
ImplForTrait::Arc,
162+
];
163+
164+
assert_eq!(impl_types, expected);
141165
}
142166

143167
#[test]
@@ -241,6 +265,49 @@ mod tests {
241265
assert_invalid(&[ImplForTrait::Arc], input, "auto impl for `Arc` is only supported for methods with a `&self` reciever");
242266
}
243267

268+
#[test]
269+
fn impl_ref() {
270+
let input = quote!(
271+
/// Some docs.
272+
pub trait ItWorks {
273+
/// Some docs.
274+
fn method1(&self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
275+
}
276+
);
277+
278+
let derive = quote!(
279+
impl<'auto, TAutoImpl> ItWorks for &'auto TAutoImpl
280+
where TAutoImpl: ItWorks
281+
{
282+
fn method1(&self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
283+
(**self).method1(arg1, arg2)
284+
}
285+
}
286+
287+
impl<'auto, TAutoImpl> ItWorks for &'auto mut TAutoImpl
288+
where TAutoImpl: ItWorks
289+
{
290+
fn method1(&self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
291+
(**self).method1(arg1, arg2)
292+
}
293+
}
294+
);
295+
296+
assert_tokens(&[ImplForTrait::Ref, ImplForTrait::RefMut], input, derive);
297+
}
298+
299+
#[test]
300+
fn invalid_ref_mut_method() {
301+
let input = quote!(
302+
pub trait ItWorks {
303+
fn method1(&mut self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
304+
fn method2(&self);
305+
}
306+
);
307+
308+
assert_invalid(&[ImplForTrait::Ref], input, "auto impl for `&T` is only supported for methods with a `&self` reciever");
309+
}
310+
244311
#[test]
245312
fn impl_fn() {
246313
let input = quote!(

src/parse.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use std::str;
2+
3+
pub fn attr<'a>(input: &'a str) -> Result<Vec<String>, String> {
4+
fn remove_whitespace(i: &[u8]) -> String {
5+
let ident = i.iter().filter(|c| **c != b' ').cloned().collect();
6+
String::from_utf8(ident).expect("non-utf8 string")
7+
}
8+
9+
fn ident(i: &[u8]) -> (String, &[u8]) {
10+
if let Some(end) = i.iter().position(|c| *c == b',') {
11+
(remove_whitespace(&i[..end]), &i[end..])
12+
}
13+
else {
14+
(remove_whitespace(i), &[])
15+
}
16+
}
17+
18+
fn attr_inner<'a>(rest: &'a [u8], traits: &mut Vec<String>) -> Result<(), String> {
19+
match rest.len() {
20+
0 => Ok(()),
21+
_ => {
22+
match rest[0] as char {
23+
'&' => {
24+
let (ident, rest) = ident(rest);
25+
traits.push(ident);
26+
27+
attr_inner(rest, traits)
28+
},
29+
c if c.is_alphabetic() => {
30+
let (ident, rest) = ident(rest);
31+
traits.push(ident);
32+
33+
attr_inner(rest, traits)
34+
},
35+
_ => attr_inner(&rest[1..], traits)
36+
}
37+
}
38+
}
39+
}
40+
41+
let mut traits = vec![];
42+
let input = input.as_bytes();
43+
44+
let open = input.iter().position(|c| *c == b'(');
45+
let close = input.iter().position(|c| *c == b')');
46+
47+
match (open, close) {
48+
(Some(open), Some(close)) => attr_inner(&input[open..close], &mut traits)?,
49+
_ => Err("attribute format should be `#[auto_impl(a, b, c)]`")?
50+
}
51+
52+
Ok(traits)
53+
}

0 commit comments

Comments
 (0)