auto_enumerate 0.1.0

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

auto_enumerate

Build Status version documentation license Rustc Version

API 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

  • #[enum_derive] - implements the specified traits

Usage

Add this to your Cargo.toml:

[dependencies]
auto_enumerate = "0.1"

and this to your crate root:

#[macro_use]
extern crate auto_enumerate;

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.

#[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(),
    }
}

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

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)
}

marker! macro

#[auto_enum] replaces marker! macros with variants.

#[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(),
    }
}

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.

#[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;
    }
}

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.
#[auto_enum(Iterator)]
fn foo(x: i32) -> impl Iterator<Item = i32> {
    match x {
        0 => 1..10,
        _ if x < 0 => panic!(), // variant assignment is skipped
        _ => vec![5, 10].into_iter(),
    }
}

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

#[auto_enum(Iterator)]
fn foo(x: i32) -> impl Iterator<Item = i32> {
    match x {
        0 => 1..10,
        #[never]
        _ if x < 0 => loop {
            panic!()
        },
        _ => vec![5, 10].into_iter(),
    }
}

Parse nested branches

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

#[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,
        },
    }
}

Supported traits

auto_enum uses #[enum_derive] attribute macro for trait implementations.

#[enum_derive] is an attribute macro like a wrapper of #[derive], implementing the supported traits and passing 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]

// `#[enum_derive]` implements `Iterator`, and `#[derive]` implements `Clone`.
#[enum_derive(Iterator, Clone)]
enum Foo<A, B> {
    A(A),
    B(B),
}

[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:

      use std::{fs, io, path::Path};
      
      #[auto_enum(Transpose, io::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()
      }
      
    • 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.

      Add this to your Cargo.toml instead:

      [dependencies]
      auto_enumerate = { version = "0.1", features = ["type_analysis"] }
      

      Examples:

      #[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(),
          }
      }
      

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

  • transpose_methods

    • Disabled by default.
    • Use transpose* methods.

Using external libraries (disabled by default)

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.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.