Skip to content

Support by-value and static methods where possible #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 9, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "auto_impl"
version = "0.1.2"
version = "0.2.0"
authors = ["Ashley Mannix <[email protected]>"]
license = "MIT"
description = "Automatically implement traits for common smart pointers and closures"
85 changes: 68 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -22,23 +22,39 @@ extern crate auto_impl;
use auto_impl::auto_impl;
```

The following types are supported:
Add an `auto_impl` attribute to traits you want to automatically implement for wrapper types:

```rust
#[auto_impl(&, Arc)]
pub trait OrderStoreFilter {
fn filter<F>(&self, predicate: F) -> Result<Iter, Error>
where
F: Fn(&OrderData) -> bool;
}
```

Now anywhere that requires a `T: OrderStoreFilter` will also accept an `&T` or `Arc<T>` where `T` implements `OrderStoreFilter`.

## Specifics

The following types are supported and can be freely combined:

- [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
- [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html)
- [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html)

The following types are also supported, but require a blanket implementation (you can only have one of these per `trait`):

- [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html)
- [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html)
- [`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html)
- `&`
- `&mut`

## Implement a trait for a smart pointer

Add the `#[auto_impl]` attribute to traits to automatically implement them for wrapper types:
## Implement a trait for `Box`

```rust
#[auto_impl(Arc, Box, Rc)]
#[auto_impl(Box)]
trait MyTrait<'a, T>
where T: AsRef<str>
{
@@ -47,41 +63,74 @@ trait MyTrait<'a, T>

fn execute1<'b>(&'a self, arg1: &'b T) -> Result<Self::Type1, String>;
fn execute2(&self) -> Self::Type2;
fn execute3(self) -> Self::Type1;
fn execute4() -> &'static str;
}
```

Will expand to:

```rust
impl<'a, T, TAutoImpl> MyTrait<'a, T> for ::std::sync::Arc<TAutoImpl>
impl<'a, T, TAutoImpl> MyTrait<'a, T> for ::std::boxed::Box<TAutoImpl>
where TAutoImpl: MyTrait<'a, T>,
T: AsRef<str>
{
type Type1 = TAutoImpl::Type1;
type Type2 = TAutoImpl::Type2;

fn execute1<'b>(&'a self, arg1: &'b T) -> Result<Self::Type1, String> {
self.as_ref().execute1(arg1)
(**self).execute1(arg1)
}

fn execute2(&self) -> Self::Type2 {
self.as_ref().execute2()
(**self).execute2()
}

fn execute3(self) -> Self::Type1 {
(*self).execute3()
}

fn execute4() -> &'static str {
TAutoImpl::execute4()
}
}
```

There are no restrictions on `auto_impl` for `Box`.

## Implement a trait for a smart pointer

Add the `#[auto_impl]` attribute to traits to automatically implement them for wrapper types:

```rust
#[auto_impl(Arc, Rc)]
trait MyTrait<'a, T>
where T: AsRef<str>
{
type Type1;
type Type2;

fn execute1<'b>(&'a self, arg1: &'b T) -> Result<Self::Type1, String>;
fn execute2(&self) -> Self::Type2;
}
```

impl<'a, T, TAutoImpl> MyTrait<'a, T> for Box<TAutoImpl>
Will expand to:

```rust
impl<'a, T, TAutoImpl> MyTrait<'a, T> for ::std::sync::Arc<TAutoImpl>
where TAutoImpl: MyTrait<'a, T>,
T: AsRef<str>
{
type Type1 = TAutoImpl::Type1;
type Type2 = TAutoImpl::Type2;

fn execute1<'b>(&'a self, arg1: &'b T) -> Result<Self::Type1, String> {
self.as_ref().execute1(arg1)
(**self).execute1(arg1)
}

fn execute2(&self) -> Self::Type2 {
self.as_ref().execute2()
(**self).execute2()
}
}

@@ -93,18 +142,18 @@ impl<'a, T, TAutoImpl> MyTrait<'a, T> for ::std::rc::Rc<TAutoImpl>
type Type2 = TAutoImpl::Type2;

fn execute1<'b>(&'a self, arg1: &'b T) -> Result<Self::Type1, String> {
self.as_ref().execute1(arg1)
(**self).execute1(arg1)
}

fn execute2(&self) -> Self::Type2 {
self.as_ref().execute2()
(**self).execute2()
}
}
```

There are a few restrictions on `#[auto_impl]` for smart pointers. The trait must:

- Only have methods that take `&self`
- Only have methods that take `&self` or have no receiver (static)

## Implement a trait for a closure

@@ -119,7 +168,7 @@ Will expand to:

```rust
impl<'a, T, TAutoImpl> MyTrait<'a, T> for TAutoImpl
where TAutoImpl: Fn(&T, &'static str) -> Result<(), String>
where TAutoImpl: ::std::ops::Fn(&T, &'static str) -> Result<(), String>
{
fn execute<'b>(&'a self, arg1: &'b T, arg1: &'static str) -> Result<(), String> {
self(arg1, arg2)
@@ -160,6 +209,8 @@ impl<'auto, 'a, T, TAutoImpl> MyTrait<'a, T> for &'auto mut TAutoImpl {

There are a few restrictions on `#[auto_impl]` for immutably borrowed references. The trait must:

- Only have methods that take `&self`
- Only have methods that take `&self` or have no receiver (static)

There are a few restrictions on `#[auto_impl]` for mutably borrowed references. The trait must:

There are no restrictions on `#[auto_impl]` for mutably borrowed references.
- Only have methods that take `&self`, `&mut self` or have no receiver (static)
15 changes: 15 additions & 0 deletions compile_test/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*!
Try running `cargo expand` on this crate to see the output of `#[auto_impl]`.
*/

#![feature(proc_macro)]

extern crate auto_impl;
@@ -28,6 +32,17 @@ trait RefTrait1<'a, T: for<'b> Into<&'b str>> {
fn execute2(&self) -> Self::Type2;
}

#[auto_impl(Box)]
trait BoxTrait1<'a, T: for<'b> Into<&'b str>> {
type Type1;
type Type2;

fn execute1<'b>(&'a self, arg1: &'b T) -> Result<Self::Type1, String>;
fn execute2(&mut self, arg1: i32) -> Self::Type2;
fn execute3(self) -> Self::Type1;
fn execute4(arg1: String) -> Result<i32, String>;
}

fn main() {
println!("Hello, world!");
}
207 changes: 148 additions & 59 deletions src/impl_as_ref.rs
Original file line number Diff line number Diff line change
@@ -1,96 +1,185 @@
use std::fmt;
use syn;
use quote::Tokens;
use model::*;

pub struct WrapperTrait {
inner: Trait,
valid_receivers: ValidReceivers,
}

impl WrapperTrait {
pub fn impl_rc() -> Self {
WrapperTrait {
inner: Trait::new("Rc", quote!(::std::rc::Rc)),
valid_receivers: ValidReceivers {
ref_self: true,
no_self: true,
..Default::default()
},
}
}

pub fn impl_arc() -> Self {
WrapperTrait {
inner: Trait::new("Arc", quote!(::std::sync::Arc)),
valid_receivers: ValidReceivers {
ref_self: true,
no_self: true,
..Default::default()
},
}
}

pub fn impl_box() -> Self {
WrapperTrait {
inner: Trait::new("Box", quote!(::std::boxed::Box)),
valid_receivers: ValidReceivers {
ref_self: true,
ref_mut_self: true,
value_self: true,
no_self: true,
},
}
}
}

pub struct RefTrait {
inner: Trait,
valid_receivers: ValidReceivers,
}

impl RefTrait {
pub fn impl_ref() -> Self {
RefTrait {
inner: Trait::new("&T", quote!(&'auto)),
valid_receivers: ValidReceivers {
ref_self: true,
no_self: true,
..Default::default()
},
}
}

pub fn impl_ref_mut() -> Self {
RefTrait {
inner: Trait::new("&mut T", quote!(&'auto mut)),
valid_receivers: ValidReceivers {
ref_self: true,
ref_mut_self: true,
no_self: true,
..Default::default()
},
}
}
}

/// Auto implement a trait for a smart pointer.
///
/// This expects the input type to have the following properties:
///
/// - The smart pointer wraps a single generic value, like `Arc<T>`, `Box<T>`, `Rc<T>`
/// - The smart pointer wraps a single generic value, like `Arc<T>`, `Rc<T>`
/// - The smart pointer implements `AsRef<T>`
pub fn build_wrapper(component: &AutoImpl, ref_ty: Trait) -> Result<Tokens, String> {
let impl_methods = component.methods.iter()
.map(|method| {
let valid_receiver = match method.arg_self {
Some(ref arg_self) => match *arg_self {
SelfArg::Ref(_, syn::Mutability::Immutable) => true,
_ => false
},
None => false
};

if !valid_receiver {
Err(format!("auto impl for `{}` is only supported for methods with a `&self` reciever", ref_ty))?
}

method.build_impl_item(|method| {
let fn_ident = &method.ident;
let fn_args = &method.arg_pats;

quote!({
self.as_ref().#fn_ident( #(#fn_args),* )
})
})
})
.collect::<Result<Vec<_>, _>>()?;
pub fn build_wrapper(component: &AutoImpl, ref_ty: WrapperTrait) -> Result<Tokens, String> {
let inner = ref_ty.inner;
let impl_ident = quote!(#inner < TAutoImpl >);

build(component, vec![], quote!(#ref_ty < TAutoImpl >), impl_methods)
build(inner, ref_ty.valid_receivers, vec![], component, impl_ident)
}

/// Auto implement a trait for an immutable reference.
///
/// This expects the input to have the following properties:
///
/// - All methods have an `&self` receiver
pub fn build_immutable(component: &AutoImpl) -> Result<Tokens, String> {
pub fn build_ref(component: &AutoImpl, ref_ty: RefTrait) -> Result<Tokens, String> {
let inner = ref_ty.inner;
let impl_ident = quote!(#inner TAutoImpl);

build(inner, ref_ty.valid_receivers, vec![quote!('auto)], component, impl_ident)
}

#[derive(Default)]
struct ValidReceivers {
ref_self: bool,
ref_mut_self: bool,
value_self: bool,
no_self: bool,
}

impl fmt::Display for ValidReceivers {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut valid_receivers = vec![];

if self.ref_self {
valid_receivers.push("`&self`");
}
if self.ref_mut_self {
valid_receivers.push("`&mut self`");
}
if self.value_self {
valid_receivers.push("`self`");
}
if self.no_self {
valid_receivers.push("static");
}

match valid_receivers.len() {
0 => unreachable!(),
1 => {
write!(f, "{}", valid_receivers[0])
}
n => {
let first = &valid_receivers[..n - 1].join(", ");
let last = &valid_receivers[n - 1];

write!(f, "{} or {}", first, last)
}
}
}
}

fn build(ref_ty: Trait, valid_receivers: ValidReceivers, extra_lifetimes: Vec<Tokens>, component: &AutoImpl, impl_ident: Tokens) -> Result<Tokens, String> {
let component_ident = &component.ident;

let impl_methods = component.methods.iter()
.map(|method| {
let valid_receiver = match method.arg_self {
Some(ref arg_self) => match *arg_self {
SelfArg::Ref(_, syn::Mutability::Immutable) => true,
_ => false
SelfArg::Ref(_, syn::Mutability::Immutable) => valid_receivers.ref_self,
SelfArg::Ref(_, syn::Mutability::Mutable) => valid_receivers.ref_mut_self,
SelfArg::Value(_) => valid_receivers.value_self,
},
None => false
None => valid_receivers.no_self
};

if !valid_receiver {
Err("auto impl for `&T` is only supported for methods with a `&self` reciever")?
Err(format!("auto impl for `{}` is only supported for methods with a {} reciever", ref_ty, valid_receivers))?
}

method.build_impl_item(|method| {
let fn_ident = &method.ident;
let fn_args = &method.arg_pats;

quote!({
(**self).#fn_ident( #(#fn_args),* )
})
})
})
.collect::<Result<Vec<_>, _>>()?;

build(component, vec![quote!('auto)], quote!(&'auto TAutoImpl), impl_methods)
}

/// Auto implement a trait for a mutable reference.
pub fn build_mutable(component: &AutoImpl) -> Result<Tokens, String> {
let impl_methods = component.methods.iter()
.map(|method| {
method.build_impl_item(|method| {
let fn_ident = &method.ident;
let fn_args = &method.arg_pats;

quote!({
(**self).#fn_ident( #(#fn_args),* )
})
match method.arg_self {
Some(ref arg_self) => match *arg_self {
// `&self` or `&mut self`
SelfArg::Ref(_, _) => quote!({
(**self).#fn_ident( #(#fn_args),* )
}),
// `self`
_ => quote!({
(*self).#fn_ident( #(#fn_args),* )
})
},
// No `self`
None => quote!({
TAutoImpl :: #fn_ident( #(#fn_args),* )
})
}
})
})
.collect::<Result<Vec<_>, _>>()?;

build(component, vec![quote!('auto)], quote!(&'auto mut TAutoImpl), impl_methods)
}

fn build(component: &AutoImpl, extra_lifetimes: Vec<Tokens>, impl_ident: Tokens, impl_methods: Vec<syn::TraitItem>) -> Result<Tokens, String> {
let component_ident = &component.ident;

let impl_associated_types = component.associated_types.iter()
.map(|associated_type| {
30 changes: 28 additions & 2 deletions src/impl_fn.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,30 @@ use syn;
use quote::Tokens;
use model::*;

pub struct FnTrait {
inner: Trait,
}

impl FnTrait {
pub fn impl_fn() -> Self {
FnTrait {
inner: Trait::new("Fn", quote!(::std::ops::Fn))
}
}

pub fn impl_fn_mut() -> Self {
FnTrait {
inner: Trait::new("FnMut", quote!(::std::ops::FnMut))
}
}

pub fn impl_fn_once() -> Self {
FnTrait {
inner: Trait::new("FnOnce", quote!(::std::ops::FnOnce))
}
}
}

/// Auto implement a trait for a function.
///
/// This expects the input type to have the following properties:
@@ -13,7 +37,9 @@ use model::*;
/// - It has a single method
/// - It has no associated type
/// - It has no non-static lifetimes in the return type
pub fn build(component: &AutoImpl, ref_ty: Trait) -> Result<Tokens, String> {
pub fn build(component: &AutoImpl, ref_ty: FnTrait) -> Result<Tokens, String> {
let ref_ty = ref_ty.inner;

let method = expect_single_method(component, &ref_ty)?;

expect_static_lifetimes_in_return_ty(&method, &ref_ty)?;
@@ -110,4 +136,4 @@ fn expect_single_method<'a>(component: &'a AutoImpl, ref_ty: &Trait) -> Result<&
let method = component.methods.iter().next().expect("");

Ok(method)
}
}
197 changes: 119 additions & 78 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@ mod impl_fn;

use std::str::FromStr;
use quote::Tokens;
use impl_as_ref::{RefTrait, WrapperTrait};
use impl_fn::FnTrait;
use model::*;

const IMPL_FOR_TRAIT_ERR: &'static str = "expected a list containing any of `&`, `&mut`, `Arc`, `Rc`, `Box`, `Fn`, `FnMut` or `FnOnce`";
@@ -44,10 +46,7 @@ impl FromStr for ImplForTrait {
"FnOnce" => Ok(ImplForTrait::FnOnce),
"&" => Ok(ImplForTrait::Ref),
"&mut" => Ok(ImplForTrait::RefMut),
c => {
println!("got: {}", c);
Err(IMPL_FOR_TRAIT_ERR)?
}
_ => Err(IMPL_FOR_TRAIT_ERR)?,
}
}
}
@@ -66,14 +65,14 @@ fn auto_impl_expand(impl_for_traits: &[ImplForTrait], tokens: Tokens) -> Result<
let impls: Vec<_> = impl_for_traits.iter()
.map(|impl_for_trait| {
match *impl_for_trait {
ImplForTrait::Arc => impl_as_ref::build_wrapper(&auto_impl, Trait::new("Arc", quote!(::std::sync::Arc))),
ImplForTrait::Rc => impl_as_ref::build_wrapper(&auto_impl, Trait::new("Rc", quote!(::std::rc::Rc))),
ImplForTrait::Box => impl_as_ref::build_wrapper(&auto_impl, Trait::new("Box", quote!(Box))),
ImplForTrait::Ref => impl_as_ref::build_immutable(&auto_impl),
ImplForTrait::RefMut => impl_as_ref::build_mutable(&auto_impl),
ImplForTrait::Fn => impl_fn::build(&auto_impl, Trait::new("Fn", quote!(Fn))),
ImplForTrait::FnMut => impl_fn::build(&auto_impl, Trait::new("FnMut", quote!(FnMut))),
ImplForTrait::FnOnce => impl_fn::build(&auto_impl, Trait::new("FnOnce", quote!(FnOnce)))
ImplForTrait::Arc => impl_as_ref::build_wrapper(&auto_impl, WrapperTrait::impl_arc()),
ImplForTrait::Rc => impl_as_ref::build_wrapper(&auto_impl, WrapperTrait::impl_rc()),
ImplForTrait::Box => impl_as_ref::build_wrapper(&auto_impl, WrapperTrait::impl_box()),
ImplForTrait::Ref => impl_as_ref::build_ref(&auto_impl, RefTrait::impl_ref()),
ImplForTrait::RefMut => impl_as_ref::build_ref(&auto_impl, RefTrait::impl_ref_mut()),
ImplForTrait::Fn => impl_fn::build(&auto_impl, FnTrait::impl_fn()),
ImplForTrait::FnMut => impl_fn::build(&auto_impl, FnTrait::impl_fn_mut()),
ImplForTrait::FnOnce => impl_fn::build(&auto_impl, FnTrait::impl_fn_once())
}
})
.collect();
@@ -115,11 +114,14 @@ mod tests {

#[test]
fn impl_types() {
let input = quote!(#[auto_impl(Arc, Box, Fn, FnMut, FnOnce)]);
let input = quote!(#[auto_impl(&, &mut, Rc, Arc, Box, Fn, FnMut, FnOnce)]);

let impls = parse_impl_types(input).unwrap();

assert_eq!(vec![
ImplForTrait::Ref,
ImplForTrait::RefMut,
ImplForTrait::Rc,
ImplForTrait::Arc,
ImplForTrait::Box,
ImplForTrait::Fn,
@@ -132,36 +134,25 @@ mod tests {
fn invalid_impl_types() {
let input = quote!(#[auto_impl(NotSupported)]);

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

assert_eq!("expected a list containing any of `&`, `&mut`, `Arc`, `Rc`, `Box`, `Fn`, `FnMut` or `FnOnce`", &impls);
assert!(impls.is_err());
}

#[test]
fn parse_attr_single() {
let input = quote!(#[auto_impl(&)]);
let impl_types = parse_impl_types(input).unwrap();
fn parse_attr_raw_single() {
let input = "#[auto_impl(&)]";
let parsed = parse::attr(input).unwrap();

let expected = vec![ImplForTrait::Ref];

assert_eq!(impl_types, expected);
assert_eq!(parsed, &["&"]);
}

#[test]
fn parse_attr_multi() {
let input = quote!(#[auto_impl(&, &mut, Arc)]);

println!("{}", input);

let impl_types = parse_impl_types(input).unwrap();

let expected = vec![
ImplForTrait::Ref,
ImplForTrait::RefMut,
ImplForTrait::Arc,
];
fn parse_attr_raw() {
let input = "#[auto_impl(&, &mut, Arc)]";
let parsed = parse::attr(input).unwrap();

assert_eq!(impl_types, expected);
assert_eq!(parsed, &["&", "&mut", "Arc"]);
}

#[test]
@@ -175,6 +166,8 @@ mod tests {
fn method2(&self) {
println!("default");
}
/// Some docs.
fn method3() -> &'static str;
}
);

@@ -183,86 +176,113 @@ mod tests {
where TAutoImpl: ItWorks
{
fn method1(&self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
self.as_ref().method1(arg1, arg2)
(**self).method1(arg1, arg2)
}
fn method2(&self) {
self.as_ref().method2()
(**self).method2()
}
fn method3() -> &'static str {
TAutoImpl::method3()
}
}

impl<TAutoImpl> ItWorks for Box<TAutoImpl>
impl<TAutoImpl> ItWorks for ::std::rc::Rc<TAutoImpl>
where TAutoImpl: ItWorks
{
fn method1(&self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
self.as_ref().method1(arg1, arg2)
(**self).method1(arg1, arg2)
}
fn method2(&self) {
self.as_ref().method2()
(**self).method2()
}
fn method3() -> &'static str {
TAutoImpl::method3()
}
}
);

assert_tokens(&[ImplForTrait::Arc, ImplForTrait::Box], input, derive);
assert_tokens(&[ImplForTrait::Arc, ImplForTrait::Rc], input, derive);
}

#[test]
fn impl_as_ref_associated_types() {
fn impl_box() {
let input = quote!(
/// Some docs.
pub trait ItWorks {
/// Some docs.
type Type1: AsRef<[u8]>;

fn method1(&self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
/// Some docs.
fn method1(&self, arg1: i32, arg2: Self::Type1) -> Result<(), String>;
fn method2(&mut self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
/// Some docs.
fn method2(&self) {
println!("default");
}
fn method3(self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
/// Some docs.
fn method4() -> &'static str;
}
);

let derive = quote!(
impl<TAutoImpl> ItWorks for ::std::sync::Arc<TAutoImpl>
impl<TAutoImpl> ItWorks for ::std::boxed::Box<TAutoImpl>
where TAutoImpl: ItWorks
{
type Type1 = TAutoImpl::Type1;

fn method1(&self, arg1: i32, arg2: Self::Type1) -> Result<(), String> {
self.as_ref().method1(arg1, arg2)
fn method1(&self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
(**self).method1(arg1, arg2)
}
fn method2(&self) {
self.as_ref().method2()
fn method2(&mut self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
(**self).method2(arg1, arg2)
}
fn method3(self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
(*self).method3(arg1, arg2)
}
fn method4() -> &'static str {
TAutoImpl::method4()
}
}
);

assert_tokens(&[ImplForTrait::Box], input, derive);
}

#[test]
fn impl_as_ref_associated_types() {
let input = quote!(
/// Some docs.
pub trait ItWorks {
/// Some docs.
type Type1: AsRef<[u8]>;
}
);

impl<TAutoImpl> ItWorks for Box<TAutoImpl>
let derive = quote!(
impl<TAutoImpl> ItWorks for ::std::sync::Arc<TAutoImpl>
where TAutoImpl: ItWorks
{
type Type1 = TAutoImpl::Type1;

fn method1(&self, arg1: i32, arg2: Self::Type1) -> Result<(), String> {
self.as_ref().method1(arg1, arg2)
}
fn method2(&self) {
self.as_ref().method2()
}
}
);

assert_tokens(&[ImplForTrait::Arc, ImplForTrait::Box], input, derive);
assert_tokens(&[ImplForTrait::Arc], input, derive);
}

#[test]
fn invalid_as_ref_mut_method() {
let input = quote!(
pub trait ItWorks {
fn method1(&mut self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
fn method2(&self);
}
);

assert_invalid(&[ImplForTrait::Arc], input, "auto impl for `Arc` is only supported for methods with a `&self` reciever");
assert_invalid(&[ImplForTrait::Arc], input, "auto impl for `Arc` is only supported for methods with a `&self` or static reciever");
}

#[test]
fn invalid_as_ref_by_value_method() {
let input = quote!(
pub trait ItWorks {
fn method1(self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
}
);

assert_invalid(&[ImplForTrait::Arc], input, "auto impl for `Arc` is only supported for methods with a `&self` or static reciever");
}

#[test]
@@ -299,13 +319,34 @@ mod tests {
#[test]
fn invalid_ref_mut_method() {
let input = quote!(
pub trait ItWorks {
pub trait ItFails {
fn method1(&mut self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
fn method2(&self);
}
);

assert_invalid(&[ImplForTrait::Ref], input, "auto impl for `&T` is only supported for methods with a `&self` reciever");
assert_invalid(&[ImplForTrait::Ref], input, "auto impl for `&T` is only supported for methods with a `&self` or static reciever");
}

#[test]
fn invalid_ref_by_value_method() {
let input = quote!(
pub trait ItFails {
fn method1(self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
}
);

assert_invalid(&[ImplForTrait::Ref], input, "auto impl for `&T` is only supported for methods with a `&self` or static reciever");
}

#[test]
fn invalid_ref_mut_by_value_method() {
let input = quote!(
pub trait ItFails {
fn method1(self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
}
);

assert_invalid(&[ImplForTrait::RefMut], input, "auto impl for `&mut T` is only supported for methods with a `&self`, `&mut self` or static reciever");
}

#[test]
@@ -320,7 +361,7 @@ mod tests {

let derive = quote!(
impl<TFn> ItWorks for TFn
where TFn: Fn(i32, Option<String>) -> Result<&'static str, String>
where TFn: ::std::ops::Fn(i32, Option<String>) -> Result<&'static str, String>
{
fn method(&self, arg1: i32, arg2: Option<String>) -> Result<&'static str, String> {
self(arg1, arg2)
@@ -343,7 +384,7 @@ mod tests {

let derive = quote!(
impl<'a, T, U, TFn> ItWorks<'a, T, U> for TFn
where TFn: Fn(i32, &U, &'static str) -> Result<T, String>,
where TFn: ::std::ops::Fn(i32, &U, &'static str) -> Result<T, String>,
U: AsRef<[u8]>
{
fn method<'b>(&'a self, arg1: i32, arg2: &'b U, arg3: &'static str) -> Result<T, String> {
@@ -365,7 +406,7 @@ mod tests {

let derive = quote!(
impl<TFn> ItWorks for TFn
where TFn: FnMut(i32, Option<String>) -> Result<(), String>
where TFn: ::std::ops::FnMut(i32, Option<String>) -> Result<(), String>
{
fn method(&mut self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
self(arg1, arg2)
@@ -386,7 +427,7 @@ mod tests {

let derive = quote!(
impl<TFn> ItWorks for TFn
where TFn: FnOnce(i32, Option<String>) -> Result<(), String>
where TFn: ::std::ops::FnOnce(i32, Option<String>) -> Result<(), String>
{
fn method(self, arg1: i32, arg2: Option<String>) -> Result<(), String> {
self(arg1, arg2)
@@ -407,7 +448,7 @@ mod tests {

let derive = quote!(
impl<TFn> ItWorks for TFn
where TFn: Fn(i32, Option<String>)
where TFn: ::std::ops::Fn(i32, Option<String>)
{
fn method(&self, arg1: i32, arg2: Option<String>) {
self(arg1, arg2)
@@ -421,7 +462,7 @@ mod tests {
#[test]
fn invalid_fn_associated_types() {
let input = quote!(
pub trait ItWorks {
pub trait ItFails {
type TypeA;
type TypeB;

@@ -435,7 +476,7 @@ mod tests {
#[test]
fn invalid_fn_lifetime_in_return_type_path() {
let input = quote!(
pub trait ItWorks {
pub trait ItFails {
fn method<'a>(&'a self) -> Result<Option<&'a str>, String>;
}
);
@@ -446,7 +487,7 @@ mod tests {
#[test]
fn invalid_fn_lifetime_in_return_type_tuple() {
let input = quote!(
pub trait ItWorks {
pub trait ItFails {
fn method<'a>(&'a self) -> Result<(&'a str, i32), String>;
}
);
@@ -457,7 +498,7 @@ mod tests {
#[test]
fn invalid_fn_no_methods() {
let input = quote!(
pub trait ItWorks {
pub trait ItFails {

}
);
@@ -468,7 +509,7 @@ mod tests {
#[test]
fn invalid_fn_multiple_methods() {
let input = quote!(
pub trait ItWorks {
pub trait ItFails {
fn method1(&self, arg1: i32, arg2: Option<String>) -> Result<(), String>;
fn method2(&self) -> String;
}
4 changes: 3 additions & 1 deletion src/parse.rs
Original file line number Diff line number Diff line change
@@ -20,12 +20,14 @@ pub fn attr<'a>(input: &'a str) -> Result<Vec<String>, String> {
0 => Ok(()),
_ => {
match rest[0] as char {
// Parse a borrowed reference
'&' => {
let (ident, rest) = ident(rest);
traits.push(ident);

attr_inner(rest, traits)
},
// Parse a regular ident
c if c.is_alphabetic() => {
let (ident, rest) = ident(rest);
traits.push(ident);
@@ -45,7 +47,7 @@ pub fn attr<'a>(input: &'a str) -> Result<Vec<String>, String> {
let close = input.iter().position(|c| *c == b')');

match (open, close) {
(Some(open), Some(close)) => attr_inner(&input[open..close], &mut traits)?,
(Some(open), Some(close)) => attr_inner(&input[open + 1 .. close], &mut traits)?,
_ => Err("attribute format should be `#[auto_impl(a, b, c)]`")?
}