Enum spirit_tokio::either::Either[][src]

pub enum Either<A, B> {
    A(A),
    B(B),
}

The Either type allows to wrap two similar Fragments and let the user choose which one will be used.

For example, if your server could run both on common TCP and unix domain stream sockets, you could use the Either<TcpListen, UnixListen>. This fragment would then create resources of type Either<TcpListener, UnixListener>.

Many traits are delegated through to one or the other instance inside (in case both implement it). So, the above resource will implement the Accept trait that will accept instances of Either<TcpStream, UnixStream>. These’ll in turn implement AsyncRead and AsyncWrite, therefore can be handled uniformly just as connections.

Deserialization

This uses the untagged serde attribute. This means there are no additional configuration options present and the choice is made by trying to first deserialize the A variant and if that fails, trying the B one. Therefore, the inner resource configs need to have some distinct fields. In our example, this would parse as TcpListen:

[[listen]]
port = 1234

While this as an UnixListen:

[[listen]]
path = "/tmp/socket"

If you need different parsing, you can use either a newtype or remote derive.

Other similar types

This is not the only Either type around. Unfortunately, none of the available ones was just right for the use case here, so this crate rolls its own. But it provides From/Into conversions between them, if the corresponding feature on this crate is enabled.

More than two options

This allows only two variants. However, if you need more, it is possible to nest them and form a tree.

Drawbacks

Due to the complexity of implementation, the Fragment is implemented for either only if both variants are Fragments with simple enough Drivers (drivers that don’t sub-divide their Fragments). Therefore, Vec<Either<TcpListen, UnixListen>> will work, but Either<Vec<TcpListen>, Vec<UnixListen>> will not.

This is an implementation limitation and may be lifted in the future (PRs are welcome).

Examples

use std::sync::Arc;

use serde::Deserialize;
use spirit::{AnyError, Empty, Pipeline, Spirit};
use spirit::prelude::*;
#[cfg(unix)]
use spirit_tokio::either::Either;
use spirit_tokio::handlers::PerConnection;
use spirit_tokio::net::TcpListen;
#[cfg(unix)]
use spirit_tokio::net::unix::UnixListen;
use tokio::io::{AsyncWrite, AsyncWriteExt};
use tokio::pin;

// If we want to work on systems that don't have unix domain sockets...

#[cfg(unix)]
type Listener = Either<TcpListen, UnixListen>;
#[cfg(not(unix))]
type Listener = TcpListen;

const DEFAULT_CONFIG: &str = r#"
[[listening_socket]]
port = 1235
max-conn = 20
error-sleep = "100ms"
"#;
#[derive(Default, Deserialize)]
struct Config {
    listening_socket: Vec<Listener>,
}

impl Config {
    fn listen(&self) -> Vec<Listener> {
        self.listening_socket.clone()
    }
}

async fn handle_connection<C: AsyncWrite>(conn: C) -> Result<(), AnyError> {
    pin!(conn);
    conn.write_all(b"hello world").await?;
    conn.shutdown().await?;
    Ok(())
}

fn main() {
    let handler = PerConnection(|conn, _cfg: &_| async {
        if let Err(e) = handle_connection(conn).await {
            eprintln!("Error: {}", e);
        }
    });
    Spirit::<Empty, Config>::new()
        .config_defaults(DEFAULT_CONFIG)
        .with(Pipeline::new("listen").extract_cfg(Config::listen).transform(handler))
        .run(|spirit| {
            Ok(())
        });
}

Variants

A(A)
B(B)

Implementations

impl<T> Either<T, T>[src]

pub fn into_inner(self) -> T[src]

Extracts the inner value in case both have the same type.

Sometimes, a series of operations produces an Either with both types the same. In such case, Either plays no role anymore and this method can be used to get to the inner value.

Trait Implementations

impl<A, B> Accept for Either<A, B> where
    A: Accept,
    B: Accept
[src]

type Connection = Either<A::Connection, B::Connection>

The type of the accepted connection.

impl<A, B> AsyncBufRead for Either<A, B> where
    A: AsyncBufRead,
    B: AsyncBufRead
[src]

impl<A, B> AsyncRead for Either<A, B> where
    A: AsyncRead,
    B: AsyncRead
[src]

impl<A, B> AsyncSeek for Either<A, B> where
    A: AsyncSeek,
    B: AsyncSeek
[src]

impl<A, B> AsyncWrite for Either<A, B> where
    A: AsyncWrite,
    B: AsyncWrite
[src]

impl<A: Clone, B: Clone> Clone for Either<A, B>[src]

impl<A, B, AR, BR> Comparable<Either<AR, BR>> for Either<A, B> where
    A: Comparable<AR>,
    B: Comparable<BR>, 
[src]

impl<A: Copy, B: Copy> Copy for Either<A, B>[src]

impl<A: Debug, B: Debug> Debug for Either<A, B>[src]

impl<'de, A, B> Deserialize<'de> for Either<A, B> where
    A: Deserialize<'de>,
    B: Deserialize<'de>, 
[src]

impl<A, B> Driver<Either<A, B>> for EitherDriver<A, B> where
    A: Fragment,
    A::Driver: Driver<A, SubFragment = A> + Default,
    B: Fragment,
    B::Driver: Driver<B, SubFragment = B> + Default
[src]

type SubFragment = Either<A, B>

The smaller Fragment the driver cuts F into. Read more

impl<A: Eq, B: Eq> Eq for Either<A, B>[src]

impl<A, B> Fragment for Either<A, B> where
    A: Fragment,
    A::Driver: Driver<A, SubFragment = A>,
    B: Fragment,
    B::Driver: Driver<B, SubFragment = B>, 
[src]

type Driver = EitherDriver<A, B>

The default driver to be used by the fragment. Read more

type Installer = EitherInstaller<A::Installer, B::Installer>

The default installer to be used unless a transformation or the user doesn’t provide one. Read more

type Seed = Either<A::Seed, B::Seed>

The intermediate product if the fragment supports two-stage creation of Resources. If not, it can be set to (). Read more

type Resource = Either<A::Resource, B::Resource>

The actual product this Fragment creates.

impl<A, B> From<Either<A, B>> for Either<A, B>[src]

impl<A, B> From<Either<A, B>> for Either<A, B>[src]

impl<A, B> Future for Either<A, B> where
    A: Future,
    B: Future
[src]

type Output = Either<A::Output, B::Output>

The type of value produced on completion.

impl<A: Hash, B: Hash> Hash for Either<A, B>[src]

impl<A, B, RA, RB, O, C> Installer<Either<RA, RB>, O, C> for EitherInstaller<A, B> where
    A: Installer<RA, O, C>,
    B: Installer<RB, O, C>, 
[src]

type UninstallHandle = Either<A::UninstallHandle, B::UninstallHandle>

A handle representing lifetime of the resource. Read more

impl<A, B> Into<Either<A, B>> for Either<A, B>[src]

impl<A, B> Into<Either<A, B>> for Either<A, B>[src]

impl<A: Ord, B: Ord> Ord for Either<A, B>[src]

impl<A: PartialEq, B: PartialEq> PartialEq<Either<A, B>> for Either<A, B>[src]

impl<A: PartialOrd, B: PartialOrd> PartialOrd<Either<A, B>> for Either<A, B>[src]

impl<A, B> Serialize for Either<A, B> where
    A: Serialize,
    B: Serialize
[src]

impl<A, B> Stackable for Either<A, B> where
    A: Stackable,
    B: Stackable
[src]

impl<A, B> Stream for Either<A, B> where
    A: Stream,
    B: Stream, 
[src]

type Item = Either<A::Item, B::Item>

Values yielded by the stream.

impl<A, B> StructDoc for Either<A, B> where
    A: StructDoc,
    B: StructDoc
[src]

impl<A, B> StructuralEq for Either<A, B>[src]

impl<A, B> StructuralPartialEq for Either<A, B>[src]

impl<'pin, A, B> Unpin for Either<A, B> where
    __Either<'pin, A, B>: Unpin
[src]

Auto Trait Implementations

impl<A, B> RefUnwindSafe for Either<A, B> where
    A: RefUnwindSafe,
    B: RefUnwindSafe

impl<A, B> Send for Either<A, B> where
    A: Send,
    B: Send

impl<A, B> Sync for Either<A, B> where
    A: Sync,
    B: Sync

impl<A, B> UnwindSafe for Either<A, B> where
    A: UnwindSafe,
    B: UnwindSafe

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<R> AsyncBufReadExt for R where
    R: AsyncBufRead + ?Sized
[src]

impl<R> AsyncReadExt for R where
    R: AsyncRead + ?Sized
[src]

impl<S> AsyncSeekExt for S where
    S: AsyncSeek + ?Sized
[src]

impl<W> AsyncWriteExt for W where
    W: AsyncWrite + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> DeserializeOwned for T where
    T: for<'de> Deserialize<'de>, 
[src]

impl<T> From<T> for T[src]

impl<T> FutureExt for T where
    T: Future + ?Sized
[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<F> IntoFuture for F where
    F: Future
[src]

type Output = <F as Future>::Output

🔬 This is a nightly-only experimental API. (into_future)

The output that the future will produce on completion.

type Future = F

🔬 This is a nightly-only experimental API. (into_future)

Which kind of future are we turning this into?

impl<T> IntoResult<T> for T[src]

impl<F> Optional for F where
    F: Stackable
[src]

impl<T> StreamExt for T where
    T: Stream + ?Sized
[src]

impl<St> StreamExt for St where
    St: Stream + ?Sized
[src]

impl<T> ToOwned for T where
    T: Clone
[src]

type Owned = T

The resulting type after obtaining ownership.

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.