exhaust 0.2.3

Trait and derive macro for working with all possible values of a type (exhaustive enumeration).
Documentation
//! `bool` is a sufficiently simple and common case that it seems worth manually writing the
//! optimal implementation, and it is not quite an enum so its implementation can’t
//! straightforwardly be generated by a macro that works for enums.
//!
//! This doesn't use the “remote derive” pattern because the derive does not support double-ended
//! iteration, and would produce a superfluous factory type.

impl crate::Exhaust for bool {
    type Iter = ExhaustBool;
    fn exhaust_factories() -> Self::Iter {
        ExhaustBool(State::FalseTrue)
    }
    crate::patterns::factory_is_self!();
}

#[derive(Clone, Debug)]
pub /*-in-private*/ struct ExhaustBool(State);

#[derive(Clone, Debug, Eq, PartialEq)]
enum State {
    /// Will yield `[false, true]`
    FalseTrue,
    /// Will yield `[false]`
    False,
    /// Will yield `[true]`
    True,
    /// Will yield `[]`
    Exhausted,
}

impl Iterator for ExhaustBool {
    type Item = bool;
    fn next(&mut self) -> Option<Self::Item> {
        use State::{Exhausted, False, FalseTrue, True};

        let (item, state) = match self.0 {
            FalseTrue => (Some(false), True),
            False => (Some(false), Exhausted),
            True => (Some(true), Exhausted),
            Exhausted => (None, Exhausted),
        };
        self.0 = state;
        item
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }

    fn fold<B, F>(mut self, init: B, mut f: F) -> B
    where
        F: FnMut(B, Self::Item) -> B,
    {
        let mut state = init;
        if matches!(self.0, State::False | State::FalseTrue) {
            state = f(state, false);
        }
        if matches!(self.0, State::True | State::FalseTrue) {
            state = f(state, true);
        }
        self.0 = State::Exhausted;
        state
    }
}

impl DoubleEndedIterator for ExhaustBool {
    fn next_back(&mut self) -> Option<Self::Item> {
        use State::{Exhausted, False, FalseTrue, True};

        let (item, state) = match self.0 {
            FalseTrue => (Some(true), False),
            False => (Some(false), Exhausted),
            True => (Some(true), Exhausted),
            Exhausted => (None, Exhausted),
        };
        self.0 = state;
        item
    }

    fn rfold<B, F>(mut self, init: B, mut f: F) -> B
    where
        F: FnMut(B, Self::Item) -> B,
    {
        let mut state = init;
        if matches!(self.0, State::True | State::FalseTrue) {
            state = f(state, true);
        }
        if matches!(self.0, State::False | State::FalseTrue) {
            state = f(state, false);
        }
        self.0 = State::Exhausted;
        state
    }
}

impl core::iter::ExactSizeIterator for ExhaustBool {
    fn len(&self) -> usize {
        match self.0 {
            State::FalseTrue => 2,
            State::False | State::True => 1,
            State::Exhausted => 0,
        }
    }
}

impl core::iter::FusedIterator for ExhaustBool {}