literator 0.2.0

Efficient conversion of iterators to human-readable strings
Documentation
//! Join adapters.

use core::{
    cell::Cell,
    fmt::{self, Display, Formatter},
};

use crate::Literator;

/// Helper for [`Literator::join()`].
pub struct Join<I, D> {
    iter: Cell<Option<I>>,
    delim: D,
}

impl<I: Copy, D: Clone> Clone for Join<I, D> {
    fn clone(&self) -> Self {
        Self {
            iter: self.iter.clone(),
            delim: self.delim.clone(),
        }
    }
}

impl<D, I> Join<I, D>
where
    D: Display,
    I: Literator,
{
    pub(super) fn new(iter: I, delim: D) -> Self {
        Join {
            iter: Cell::new(Some(iter)),
            delim,
        }
    }

    pub(crate) fn do_fmt<F>(&self, f: &mut Formatter, fmt_item: F) -> fmt::Result
    where
        F: Fn(&I::Item, &mut Formatter) -> fmt::Result,
    {
        let Some(mut iter) = self.iter.take() else {
            return Ok(());
        };

        if let Some(first) = iter.next() {
            fmt_item(&first, f)?;
            for item in iter {
                write!(f, "{}", self.delim)?;
                fmt_item(&item, f)?;
            }
        }
        Ok(())
    }
}

/// Helper for [`Literator::conjunctive_join_custom()`] and friends.
pub struct ConjunctiveJoin<I, D = &'static str, L = &'static str> {
    iter: Cell<Option<I>>,
    delim: D,
    last: L,
}

impl<I, D, L> Clone for ConjunctiveJoin<I, D, L>
where
    I: Copy,
    D: Clone,
    L: Clone,
{
    fn clone(&self) -> Self {
        Self {
            iter: self.iter.clone(),
            delim: self.delim.clone(),
            last: self.last.clone(),
        }
    }
}

impl<I, D, L> ConjunctiveJoin<I, D, L>
where
    I: Literator,
    D: Display,
    L: Display,
{
    pub(super) fn new(iter: I, delim: D, last: L) -> Self {
        Self {
            iter: Cell::new(Some(iter)),
            delim,
            last,
        }
    }

    pub(crate) fn do_fmt<F>(&self, f: &mut Formatter, fmt_item: F) -> fmt::Result
    where
        F: Fn(&I::Item, &mut Formatter) -> fmt::Result,
    {
        let Self { iter, delim, last } = self;
        let Some(mut iter) = iter.take() else {
            return Ok(());
        };

        let Some(first) = iter.next() else {
            return Ok(());
        };

        fmt_item(&first, f)?;

        let Some(mut current) = iter.next() else {
            // Exactly one item.
            return Ok(());
        };

        while let Some(lookahead) = iter.next() {
            write!(f, "{delim}")?;
            fmt_item(&current, f)?;
            current = lookahead;
        }

        write!(f, "{last}")?;
        fmt_item(&current, f)
    }
}

/// Helper for [`Literator::oxford_join_custom()`] and friends.
pub struct OxfordJoin<
    I,
    Delim = &'static str,
    ExactlyTwo = &'static str,
    FinalDelimConjunction = &'static str,
> {
    iter: Cell<Option<I>>,
    sep: Delim,
    two_sep: ExactlyTwo,
    last_sep: FinalDelimConjunction,
}

impl<I, D1, D2, D3> Clone for OxfordJoin<I, D1, D2, D3>
where
    I: Copy,
    D1: Clone,
    D2: Clone,
    D3: Clone,
{
    fn clone(&self) -> Self {
        Self {
            iter: self.iter.clone(),
            sep: self.sep.clone(),
            two_sep: self.two_sep.clone(),
            last_sep: self.last_sep.clone(),
        }
    }
}

impl<I, D1, D2, D3> OxfordJoin<I, D1, D2, D3>
where
    I: Literator,
    D1: Display,
    D2: Display,
    D3: Display,
{
    pub(super) fn new(iter: I, sep: D1, two_sep: D2, last_sep: D3) -> Self {
        Self {
            iter: Cell::new(Some(iter)),
            sep,
            two_sep,
            last_sep,
        }
    }

    pub(crate) fn do_fmt<F>(&self, f: &mut Formatter, fmt_item: F) -> fmt::Result
    where
        F: Fn(&I::Item, &mut Formatter) -> fmt::Result,
    {
        let Self {
            iter,
            sep,
            two_sep,
            last_sep,
        } = self;

        let Some(mut iter) = iter.take() else {
            return Ok(());
        };

        let Some(first) = iter.next() else {
            // Zero elements
            return Ok(());
        };

        fmt_item(&first, f)?;

        let Some(second) = iter.next() else {
            // Exactly one element.
            return Ok(());
        };

        let mut lookahead = iter.next();
        if lookahead.is_none() {
            // Exactly two elements.
            write!(f, "{two_sep}")?;
            return fmt_item(&second, f);
        }

        // Three or more elements.
        let mut next = second;
        while let Some(next_next) = lookahead {
            write!(f, "{sep}")?;
            fmt_item(&next, f)?;
            next = next_next;
            lookahead = iter.next();
        }

        // Write the last element.
        write!(f, "{last_sep}")?;
        fmt_item(&next, f)
    }
}