auto_enumerate 0.1.2

crate renamed to [auto_enums](https://crates.io/crates/auto_enums)
Documentation

A library for to allow multiple return types by automatically generated enum.

This library provides the following attribute macros:

  • #[auto_enum]

Parses syntax, creates the enum, inserts variants, and passes specified traits to #[enum_derive].

  • #[enum_derive]

Implements traits received from #[auto_enum].

Examples

#[auto_enum]'s basic feature is to wrap the value returned by the last if or match expression by an enum that implemented the specified traits.

Generated code

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(Iterator)] // generats an enum with two variants
fn foo(x: i32) -> impl Iterator<Item = i32> {
match x {
0 => 1..10,
_ => vec![5, 10].into_iter(),
}
}
# fn main() { let _ = foo(0); }

You can also use #[auto_enum] for expressions and statements.

Generated code

# #[macro_use]
# extern crate auto_enumerate;
use std::{fs, io, path::Path};

#[auto_enum]
fn output_stream(file: Option<&Path>) -> io::Result<impl io::Write> {
#[auto_enum(io::Write)]
let writer = match file {
Some(f) => fs::File::create(f)?,
None => io::stdout(),
};

Ok(writer)
}
# fn main() { let _ = output_stream(None); }

Expression level marker (marker! macro)

#[auto_enum] replaces marker! macros with variants.

Generated code

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(Iterator)] // generats an enum with three variants
fn foo(x: i32) -> impl Iterator<Item = i32> {
if x < 0 {
return marker!(x..=0);
}
match x {
0 => 1..10,
_ => vec![5, 10].into_iter(),
}
}
# fn main() { let _ = foo(0); }

Also, if values of two or more are specified by marker! macros, #[auto_enum] can be used for a expression or statement that does not end with a if or match expression.

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(Iterator)]
fn foo(mut x: i32) -> impl Iterator<Item = i32> {
loop {
if x < 0 {
break marker!(x..0);
} else if x % 5 == 0 {
break marker!(0..=x);
}
x -= 1;
}
}
# fn main() { let _ = foo(0); }

The default name of the macro is "marker", but you can change it by marker option.

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(marker(bar), Iterator)]
fn foo(x: i32) -> impl Iterator<Item = i32> {
if x < 0 {
return bar!(x..=0);
}
bar!(1..10)
}
# fn main() { let _ = foo(0); }

Expression that no value will be returned

If the last expression of a branch is one of the following, it is interpreted that no value will be returned (variant assignment is skipped).

  • panic!(..)
  • unreachable!(..)
  • return
  • break
  • continue
  • None?
  • Err(..)?
  • Expression level marker (marker! macro).
  • An item definition.
# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(Iterator)]
fn foo(x: i32) -> impl Iterator<Item = i32> {
match x {
0 => 1..10,
1 => panic!(), // variant assignment is skipped
_ => vec![5, 10].into_iter(),
}
}
# fn main() { let _ = foo(0); }

You can also skip that branch explicitly by #[never] attribute.

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(Iterator)]
fn foo(x: i32) -> impl Iterator<Item = i32> {
match x {
0 => 1..10,
#[never]
1 => loop {
panic!()
},
_ => vec![5, 10].into_iter(),
}
}
# fn main() { let _ = foo(0); }

You can also skip all branches by never option.

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(never, Iterator)]
fn foo(x: i32) -> impl Iterator<Item = i32> {
match x {
0 => loop {
return marker!(1..10);
},
1 => loop {
panic!()
},
_ => loop {
return marker!(vec![5, 10].into_iter());
},
}
}
# fn main() { let _ = foo(0); }

Blocks and unsafe blocks

Blocks and unsafe blocks are parsed recursively. However, empty blocks are handled in the same as other expressions.

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(Iterator)]
fn foo(x: i32) -> impl Iterator<Item = i32> {
{
// The last expression in the block is interpreted as
// the last expression in the function.
match x {
0 => 1..10,
_ => vec![5, 10].into_iter(),
}
}
}
# fn main() { let _ = foo(0); }

Parse nested branches

You can parse nested branches by #[rec] attribute.

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(Iterator)]
fn foo(x: i32) -> impl Iterator<Item = i32> {
match x {
0 => 1..10,
#[rec]
_ => match x {
1 => vec![5, 10].into_iter(),
_ => 0..=x,
},
}
}
# fn main() { let _ = foo(0); }

Rust Nightly

When using #[auto_enum] for expressions and statements, #[auto_enum] for function is unnecessary.

# #![cfg(feature = "unstable")]
// Add this to your crate root:
#![feature(proc_macro_hygiene, stmt_expr_attributes)]
# fn main() {}
# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #![cfg_attr(feature = "unstable", feature(proc_macro_hygiene, stmt_expr_attributes))]
# #[cfg(feature = "unstable")]
# #[macro_use]
# extern crate auto_enumerate;
# #[cfg(feature = "unstable")]
fn foo(x: i32) -> i32 {
#[auto_enum(Iterator)]
let iter = match x {
0 => 1..10,
_ => vec![5, 10].into_iter(),
};

iter.fold(0, |sum, x| sum + x)
}
# #[cfg(feature = "unstable")]
# fn main() { let _ = foo(0); }
# #[cfg(not(feature = "unstable"))]
# fn main() {}

You can also return closures.

# #![cfg(feature = "unstable")]
// Add this to your crate root:
#![feature(fn_traits, unboxed_closures)]
# fn main() {}
# #![cfg_attr(feature = "unstable", feature(fn_traits, unboxed_closures))]
# #[cfg(feature = "unstable")]
# #[macro_use]
# extern crate auto_enumerate;
# #[cfg(feature = "unstable")]
#[auto_enum(Fn)]
fn foo(x: bool) -> impl Fn(i32) -> i32 {
if x {
|y| y + 1
} else {
|z| z - 1
}
}
# #[cfg(feature = "unstable")]
# fn main() { let _ = foo(false); }
# #[cfg(not(feature = "unstable"))]
# fn main() {}

Supported traits

#[enum_derive] implements the supported traits and passes unsupported traits to #[derive].

If you want to use traits that are not supported by #[enum_derive], you can use another crate that provides proc_macro_derive, or you can define proc_macro_derive yourself.

Basic usage of #[enum_derive]

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
// `#[enum_derive]` implements `Iterator`, and `#[derive]` implements `Clone`.
#[enum_derive(Iterator, Clone)]
enum Foo<A, B> {
A(A),
B(B),
}
# fn main() { let _: Foo<i32, i32> = Foo::A(0); }

#[enum_derive] adds the dependency of the specified trait if it is not specified.

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #![cfg_attr(feature = "exact_size_is_empty", feature(exact_size_is_empty))]
# #[macro_use]
# extern crate auto_enumerate;
// `#[enum_derive]` implements `Iterator` and `ExactSizeIterator`.
#[enum_derive(ExactSizeIterator)]
enum Foo<A, B> {
A(A),
B(B),
}
# fn main() { let _: Foo<i32, i32> = Foo::A(0); }

[std|core] libraries

Note that some traits have aliases.

[std|core]::ops

[std|core]::convert

[std|core]::iter

[std|core]::fmt

[std|core]::future

std::io

  • Read (alias: io::Read)
  • BufRead (alias: io::BufRead)
  • Write (alias: io::Write)
  • Seek (alias: io::Seek)

std::error

External libraries

futures(v0.3) (requires "futures" crate feature)

futures(v0.1) (requires "futures01" crate feature)

quote (requires "proc_macro" crate feature)

rayon (requires "rayon" crate feature)

serde (requires "serde" crate feature)

  • serde::Serialize - note that it is a different implementation from #[derive(Serialize)].

Static methods

These don't derive traits, but derive static methods instead.

  • Transpose (requires "transpose_methods" crate feature) - this derives the following conversion methods.

  • transpose - convert from enum<Option<T1>,..> to Option<enum<T1,..>>

  • transpose - convert from enum<Result<T1, E1>,..> to Result<enum<T1,..>, enum<E1,..>>

  • transpose_ok - convert from enum<Result<T1, E>,..> to Option<enum<T1,..>, E>

Examples:

# #[cfg(feature = "transpose_methods")]
# #[macro_use]
# extern crate auto_enumerate;
# #[cfg(feature = "transpose_methods")]
use std::{fs, io, path::Path};

# #[cfg(feature = "transpose_methods")]
#[auto_enum(Transpose, Write)]
fn output_stream(file: Option<&Path>) -> io::Result<impl io::Write> {
match file {
Some(f) => fs::File::create(f),
None => Ok(io::stdout()),
}.transpose_ok()
}
# #[cfg(feature = "transpose_methods")]
# fn main() { let _ = output_stream(None); }
# #[cfg(not(feature = "transpose_methods"))]
# fn main() {}
  • transpose_err - convert from enum<Result<T, E1>,..> to Result<T, enum<E1,..>>

Crate Features

  • std

  • Enabled by default.

  • Disable to use no_std instead.

  • type_analysis

  • Disabled by default.

  • Analyze return type of function and let binding.

Examples:

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[cfg(feature = "type_analysis")]
# #[macro_use]
# extern crate auto_enumerate;
# #[cfg(feature = "type_analysis")]
#[auto_enum] // there is no need to specify std library's traits
fn foo(x: i32) -> impl Iterator<Item = i32> {
match x {
0 => 1..10,
_ => vec![5, 10].into_iter(),
}
}
# #[cfg(feature = "type_analysis")]
# fn main() { let _ = foo(0); }
# #[cfg(not(feature = "type_analysis"))]
# fn main() {}

Please be careful if you return another traits with the same name.

  • transpose_methods

  • Disabled by default.

  • Use transpose* methods.

  • unstable

  • Disabled by default.

  • Use unstable features to make attribute macros more effective.

  • The traits supported by #[enum_derive] are not related to this feature.

  • This requires Rust Nightly.

Using external libraries (disabled by default)

Enable unstable features of [std|core] libraries (disabled by default, nightly-only)

For these features, you need to enable the unstable feature gate of the same name.

Generated code

There are two steps to generating code.

When using #[auto_enum] for function like the following:

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
#[auto_enum(Iterator, Clone)]
fn foo(x: i32) -> impl Iterator<Item = i32> + Clone {
match x {
0 => 1..10,
_ => vec![5, 10].into_iter(),
}
}
# fn main() { let _ = foo(0); }

First, #[auto_enum] will do the following.

  • parses syntax
  • creates the enum
  • inserts variants

Code like this will be generated:

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
fn foo(x: i32) -> impl Iterator<Item = i32> + Clone {
#[enum_derive(Iterator, Clone)]
enum __Enum1<__T1, __T2> {
__T1(__T1),
__T2(__T2),
}

match x {
0 => __Enum1::__T1(1..10),
_ => __Enum1::__T2(vec![5, 10].into_iter()),
}
}
# fn main() { let _ = foo(0); }

Next, #[enum_derive] implements the specified traits. Clone is not directly supported by #[enum_derive], so it passing to #[derive].

Code like this will be generated:

# #![cfg_attr(feature = "try_trait", feature(try_trait))]
# #[macro_use]
# extern crate auto_enumerate;
fn foo(x: i32) -> impl Iterator<Item = i32> + Clone {
#[derive(Clone)]
enum __Enum1<__T1, __T2> {
__T1(__T1),
__T2(__T2),
}

impl<__T1, __T2> ::std::iter::Iterator for __Enum1<__T1, __T2>
where
__T1: ::std::iter::Iterator,
__T2: ::std::iter::Iterator<Item = <__T1 as ::std::iter::Iterator>::Item>,
{
type Item = <__T1 as ::std::iter::Iterator>::Item;
fn next(&mut self) -> ::std::option::Option<Self::Item> {
match self {
__Enum1::__T1(x) => x.next(),
__Enum1::__T2(x) => x.next(),
}
}
fn size_hint(&self) -> (usize, ::std::option::Option<usize>) {
match self {
__Enum1::__T1(x) => x.size_hint(),
__Enum1::__T2(x) => x.size_hint(),
}
}
}

match x {
0 => __Enum1::__T1(1..10),
_ => __Enum1::__T2(vec![5, 10].into_iter()),
}
}
# fn main() { let _ = foo(0); }

Known limitations

  • There needs to explicitly specify the trait to be implemented (type_analysis crate feature reduces this limitation).

  • There needs to be marker macros for expressions other than match and if.

Rust Version

The current minimum required Rust version is 1.30.