dynify 0.1.2

Add dyn compatible variant to your async trait
Documentation
A helper macro to create dyn compatible variants for traits.

`#[dynify]` accepts a trait or function as input and returns it as is, along
with a *dynified* variant. This involves converting each input function into a
function constructor. Consider the following example:

```rust
# use dynify::dynify;
#[dynify]
trait Client {
    async fn request(&self, uri: &str) -> String;
}
```

It expands to something like this:

```rust
# use core::future::Future;
# use dynify::{Fn, from_fn};
trait Client {
    async fn request(&self, uri: &str) -> String;
}
// A dynified trait is generated and implemented for any type that implements
// `Client`. Although this can be done without this macro, it helps stay
// synchronized with the original trait, eliminating the need to handle elided
// lifetimes, which can be quite annoying for signatures with many lifetimes.
trait DynClient {
    fn request<'this, 'uri, 'dynify>(
        &'this self,
        uri: &'uri str,
    ) -> Fn!(&'this Self, &'uri str => dyn 'dynify + Future<Output = String>)
    where
        'this: 'dynify,
        'uri: 'dynify;
}
impl<ClientImplementor: Client> DynClient for ClientImplementor {
    fn request<'this, 'uri, 'dynify>(
        &'this self,
        uri: &'uri str,
    ) -> Fn!(&'this Self, &'uri str => dyn 'dynify + Future<Output = String>)
    where
        'this: 'dynify,
        'uri: 'dynify,
    {
        from_fn!(ClientImplementor::request, self, uri)
    }
}
```

## Customizing the generated traits

You can specify an identifier as the name of the generated trait:

```rust
# use dynify::dynify;
#[dynify(MyDynClient)]
trait Client {
    async fn request(&self, uri: &str) -> String;
}
async fn run(client: &dyn MyDynClient) {
    // ...
}
```

The identifier must be supplied as the first argument.

## Lifetime conventions

The core feature of `#[dynify]` is the expansion of
[elided lifetimes](https://doc.rust-lang.org/nomicon/lifetime-elision.html).
`#[dynify]` employs a deterministic method for this:

1. The name of each elided lifetime is based on the name of the argument that
   contains it. Therefore, the binding pattern of each argument must be an
   identifier.
2. If a lifetime is the only one, it is named exactly the same as the argument.
3. Otherwise, an index is appended to each lifetime. The index is zero-based and
   corresponds to the order in which a lifetime occurs. Both elided and named
   lifetimes are taken into account when counting indices.
4. For a method receiver, the expanded lifetime is always named based on `this`
   instead of `self`. Rule *(3)* applies in this case as well.
5. If the return type is a dyn object, it always has a bound named `'dynify`.

The following demonstrates how these rules work:

```rust
# use dynify::Fn;
# use core::future::Future;
//#[dynify::dynify]
trait Trait {
    async fn method(&self, foo: &str, bar: (&str, &str)) -> String;
}
trait DynTrait {
    fn method<'this, 'foo, 'bar0, 'bar1, 'dynify>(
        &'this self,
        foo: &'foo str,
        bar: (&'bar0 str, &'bar1 str),
    ) -> Fn!(
        &'this Self,
        &'foo str,
        (&'bar0 str, &'bar1 str)
        => dyn 'dynify + Future<Output = String>)
    where
        'this: 'dynify,
        'foo : 'dynify,
        'bar0: 'dynify,
        'bar1: 'dynify,
        // Add extra bounds here
    ;
}
```

In common cases, you can rely on the lifetimes generated by `#[dynify]`, adding
extra bounds as needed.

## Making generated traits [`Send`]able

Unlike `async-trait`, this macro does not provide support for adding `Send`
bounds to returned `Future`s (or any other `impl Trait`s). However, as
illustrated below, you can combine `#[dynify]` with
[trait-variant](https://crates.io/crates/trait-variant) to achieve this:

```rust
# use dynify::{Dynify, dynify};
# use std::mem::MaybeUninit;
// You can also use `#(make(SendClient: Send))`. However, in this case, you can
// no longer specify a name in `#[dynify]` because `#[trait_variant::make]`
// will generate two traits, which leads to conflicting trait definitions.
#[trait_variant::make(Send)]
#[dynify] // must be put within the scope of `#[trait_variant::make]`
trait Client {
    async fn request(&self, uri: &str) -> String;
}
fn run_client(
    client: &(dyn DynClient + Sync),
) -> impl '_ + std::future::Future<Output = ()> + Send {
    let mut stack = [MaybeUninit::<u8>::uninit(); 16];
    let mut heap = Vec::<MaybeUninit<u8>>::new();
    async move {
        client.request("http://magic/request").init2(&mut stack, &mut heap).await;
    }
}
```

As an alternative, you can also [bitte](https://crates.io/crates/bitte) for this
purpose:

<!-- TODO: enable doctest after we move to edition 2024 -->

```rust,ignore
# use dynify::{PinDynify, dynify};
#[bitte::bitte(Send)]
#[dynify] // must be put within the scope of `#[bitte]`
trait Client {
    async fn request(&self, uri: &str) -> String;
}
```

## Working with remote items

Suppose you're going to add a variant for a remote trait, for example:

```rust
// external_crate::
pub trait Read {
    async fn read_to_string(&mut self) -> String;
}
```

You need to copy the trait definition and specify the path to that trait using
`#[dynify(remote = "path::to::trait")]`:

```rust
# use dynify::dynify;
# mod external_crate {
#     pub trait Read { async fn read_to_string(&mut self) -> String; }
# }
#[dynify(remote = "external_crate::Read")]
// It's not necessary to include all the items; you can filter out unused ones.
pub(crate) trait DynRead {
    async fn read_to_string(&mut self) -> String;
}
```

This also works for remote functions:

```rust
# use dynify::dynify;
# mod external_crate {
#   pub async fn read_to_string(path: &str) -> String { todo!() }
# }
#[dynify(remote = "external_crate::read_to_string")]
pub(crate) async fn dyn_read_to_string(path: &str) -> String {
    /* the body of this local function doesn't matter */
}
```