trait-enumizer

img

Generate enum from a trait, with converters between them.

Features

The library can be used as a synchronisation mechanism or as a building block to build actors or remote procedure calls.

Main piece of library is enumizer attribute macro. It should be attached to trait or inherent impl. It copies input trait or impl to proc macro output (sans pseudo-derive-helpers attributes), then also generates the following items:

Parameters

#[trait_enumizer::enumizer(...)] accepts following parameters:

Example of attributes syntax:

```rust,ignore

[traitenumizer::enumizer(pubcrate, name=NewEnumName, callfn(name=callme,refmut,allowpanic), proxy(name=NewEnumNameProxy, Fn,unwrapping_impl))]

```

Call functions (call_fns)

Call functions are generated when you use call_fn() parameter. They use the following subparameters:

Those functions are used to "convert" enum value into a method call. Call functions are generated as inherent impl functions of the generated enum. First argument is self. Second argument is the value of (or reference to) something implementing the trait you specified (skipping the trait in inherent_impl mode). Third argument is required if you specify extra_arg_type(). It is passed to returnval's send (or send_async) pseudomethod for customized handling of return values.

Example:

```rust,ignore

[enumizer(name=QqqEnum,pub,callfn(name=thecall,ref_mut))]

trait Qqq { ... } ```

generates

rust,ignore enum QqqEnum { ... } impl QqqEnum { pub fn the_call<I: Qqq>(self, o: &mut I); }

Proxies

Proxies are generated when you use proxy() parameter. They use the following subparameters:

A proxy is a generic tuple struct with a public field. That field should implement Fn, FnMut or FnOnce. Second field (also public) is created if you specify extra_field_type(). There are two generic parameters: error type (you choose it) and closure type. Proxies allow "converting" method calls to enum values (which get delivered to your closure). By default all input methods are renamed, having "try_" prepended. Typically they return Result<(), YourErrorType>, but in returnval mode some of them may return Result<Result<T, SendError>, YourErrorType>. There is async mode, which upgrades your function to return Future and makes all the try_* methods async. You can ask Enumizer to also generate "resultified" trait which proxy then implements (unless async, of course). async also affects returnval macro usage.

You can also ask Enumizer to make proxy implement the original trait (also unless async). There are two strategies for it: infallible (if return values are not used and your Fn opts out of error handling by using std::convert::Infallible) and unwrapping.

You can make async proxy for non-async original methods and vice versa.

Example (simplified):

```rust,ignore

[enumizer(name=QqqEnum,proxy(FnMut,name=QqqProxy))]

trait Qqq { fn foo(&self,x : i32); } ```

generates

text enum QqqEnum { Foo{x:i32} } struct QqqProxy<E,F>(pub F) where F: FnMut(QqqEnum) -> Result<(), E>; impl<E,F> QqqProxy where ... { fn try_foo(&mut self, x: i32) -> Result<(), E>{ (self.0)(QqqEnum::Foo{x}) } }

Pseudo-derive-helpers

Pseudo-derive-helpers are attributes that are handled by this library. You are supposed to used them inside your input trait or impl before method signature or before argument inside signature. Other (unknown) attributes are passed though unmodified.

Returnval pseudotrait

If you want Enumizer to handle return values, you need a channel of some sort. Enumizer is flexible in channel choice. There are built in "classes" for some popular channel types, you may also need to implement a channel class yourself.

You specify channel class as value for returnval parameter, e.g. returnval=trait_enumizer::flume_class. Early in Enumizer design channel classes were traits using GAT, but now they are special macro_rules-based macros.

Here is API of a channel class:

rust,ignore macro_rules! my_channelclass { (Sender<$T:ty>) => { /* type of the `ret` field in enum variant */ }; (SendError) => { /* Error type when failed to send to a channel. Must not depend on T */ }; (RecvError) => { /* Error type when failed to receive from a channel */ }; (create::<$T:ty>()) => { /* Expression returning (tx, rx) channel pair. `tx` must be of type `Sender`. */ /* Used by proxies */ }; (send::<$T:ty>($channel:expr, $msg:expr /*, $extraarg:expr */)) => { /* Expression used to send to the channel (for `call_fn`). You may need to map error type here */ }; (recv::<$T:ty>($channel:expr /*, $extrafield:expr */)) => { /* Expression use to recv value from the channel (for proxy) */ }; (send_async::<$T:ty>($channel:expr, $msg:expr)) => { /* Expression to send to channel from async `call_fn`s. Should include `.await` and error mapping */ }; (recv_async::<$T:ty>($channel:expr)) => { /* Expression to receive from cahnnel in async proxies. Should include `.await`. */ }; }

You are recommended to base your implementation on one of the built-in channel class (e.g. flume_class) or to use RPC sample as a template for trickier channel class.

Although returnval mechanism use "channel" terminology, Senders are not required to be actual channels. They may be some internal IDs, with the real channel being supplied as an additional argument.

When Enumizer encountres a method with return value, corresponding enum variant gains additional field named ret (a hard coded identifier). Type of this field is controlled by the channel class and may depend on the type of the return value. All interactions with this additional field go though channel class's pseudomethods.

Tests (also serve as documentation)

To understand how the crate functions, you can view some test files. For most samples there is corresponding "manual" sample, showing expanded version (sometimes slightly simplified) of the same test.

Note that those links may be broken on Docs.rs, use README on Github instead.

Cargo features

Most features enable respective channel classes. std crate feature, which is enabled by default, apart from enabling stdmpsc channel class also enables enumizer_to_owned through ::std::borrow::ToOwned trait. alloc feature enables alternative enumizer_to_owned mode using ::alloc::borrow::ToOwned instead (but you need to declare extern crate alloc; yourself). std supersedes alloc.

See also