io-tether 0.1.2

Traits for defining I/O objects which automatically reconnect upon failure.
Documentation
# io-tether

<p align="center">
  <img src="https://cdn.akamai.steamstatic.com/apps/dota2/images/dota_react/abilities/wisp_tether.png" />
</p>


Traits for defining I/O objects which automatically reconnect upon failure.

[Crates.io](https://crates.io/crates/io-tether) |
[API Docs](https://docs.rs/io-tether/latest/io_tether/) 

This project is similar in scope to
[stubborn-io](https://github.com/craftytrickster/stubborn-io), but aims to
leverage the recently stabilized `async fn` in traits, to make the
implementation of reconnecting simpler for the end user.

## Usage

To get started, add `io-tether` to your list of dependencies

```toml
io-tether = { version = "0.1.0" }
```

Then in most cases, it is expected that the consumer of this library will want
to implement `TetherResolver` on their own types. This allows them to inject
arbitrary asynchronous code just before the I/O attempts to reconnect.

```rust
use io_tether::{TetherResolver, Context, State, Tether};
use tokio::{net::TcpStream, io::{AsyncReadExt, AsyncWriteExt}, sync::mpsc};

/// Custom resolver
pub struct CallbackResolver {
    channel: mpsc::Sender<String>,
}

impl TetherResolver for CallbackResolver {
    type Error = std::io::Error;

    async fn disconnected(
        &mut self,
        context: &Context,
        state: &State<Self::Error>,
    ) -> bool {
        match state {
            State::Eof => false, // No reconnection attempt will be made
            State::Err(error) => {
                let error = error.to_string();
                self.channel.send(error).await.unwrap();
                true
            }
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut buf = Vec::new();
    let (channel, rx) = mpsc::channel(10);

    let listener = tokio::net::TcpListener::bind("localhost:8080").await?;
    tokio::spawn(async move {
        loop {
            let (mut stream, _addr) = listener.accept().await.unwrap();
            stream.write_all(b"foo-bar").await.unwrap();
            stream.shutdown().await.unwrap();
        }
    });

    let resolver = CallbackResolver {
        channel,
    };
    let mut tether = Tether::<_, TcpStream, _>::connect("localhost:8080", resolver)
        .await?;

    tether.read_to_end(&mut buf).await?;
    
    assert_eq!(&buf, b"foo-bar");

    Ok(())
}
```

## Stability

This project is still very much a work in progress. Expect fairly common 
breaking changes in the short term.