literator 0.3.0

Efficient conversion of iterators to human-readable strings
Documentation
use core::{
    cell::Cell,
    fmt::{Debug, Display},
};

use crate::{Literator, block::*, decorate::*, fmt::*, join::*};

/// Adds `Display`/`Debug` formatting to any iterator whose items implement
/// `Display` and/or `Debug`.
///
/// All formatting arguments are passed through to each item's `Display`/`Debug`
/// implementation.
pub struct Literate<I> {
    iter: Cell<Option<I>>,
}

impl<I: Sized> Literate<I> {
    /// Wrap an iterator in a `Literate` struct.
    pub const fn new(iter: I) -> Self {
        Literate {
            iter: Cell::new(Some(iter)),
        }
    }

    /// Consumes the `Literate` and returns the inner iterator. Returns `None`
    /// if the object has been formatted already.
    pub fn into_inner(self) -> Option<I> {
        self.iter.take()
    }

    /// Convert the inner iterator to another inner iterator.
    pub fn map_iterator<F, O>(self, f: F) -> Literate<O>
    where
        F: FnOnce(I) -> O,
    {
        if let Some(iter) = self.iter.take() {
            Literate::new(f(iter))
        } else {
            Literate {
                iter: Cell::new(None),
            }
        }
    }

    fn map_literator<F, O>(self, f: F) -> Literate<O>
    where
        F: FnOnce(I) -> Literate<O>,
    {
        if let Some(iter) = self.iter.take() {
            f(iter)
        } else {
            Literate {
                iter: Cell::new(None),
            }
        }
    }
}

impl<I: Clone> Clone for Literate<I> {
    fn clone(&self) -> Self {
        if let Some(value) = self.iter.take() {
            let cloned = value.clone();
            self.iter.set(Some(value)); // Put the original value back
            Literate::new(cloned)
        } else {
            Literate {
                iter: Cell::new(None),
            }
        }
    }
}

impl<I> Display for Literate<I>
where
    I: Iterator<Item: Display>,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let Some(mut iter) = self.iter.take() else {
            return Ok(());
        };
        while let Some(item) = iter.next() {
            item.fmt(f)?;
        }
        Ok(())
    }
}

impl<I> Debug for Literate<I>
where
    I: Iterator<Item: Debug>,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let Some(mut iter) = self.iter.take() else {
            return Ok(());
        };
        while let Some(item) = iter.next() {
            item.fmt(f)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloc::format;

    struct Foo;
    impl Display for Foo {
        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
            f.write_str("display")
        }
    }
    impl Debug for Foo {
        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
            f.write_str("debug")
        }
    }

    #[test]
    fn forward_display_debug() {
        assert_eq!(
            format!("{}", Literate::new([Foo, Foo].iter())),
            "displaydisplay"
        );
        assert_eq!(
            format!("{:?}", Literate::new([Foo, Foo].iter())),
            "debugdebug"
        );
    }
}

impl<I> Literator for Literate<I>
where
    I: Iterator + Sized,
{
    type Item = I::Item;
    type Iter = I;

    fn join<D>(self, delim: D) -> Join<Self::Iter, D>
    where
        Self: Sized,
        D: Display,
    {
        Join::new_cell(self.iter.take(), delim)
    }

    fn concat(self) -> Self
    where
        Self: Sized,
    {
        self
    }

    fn conjunctive_join_custom<Delim, LastDelim>(
        self,
        delim: Delim,
        last_delim: LastDelim,
    ) -> ConjunctiveJoin<Self::Iter, Delim, LastDelim>
    where
        Self: Sized,
        Delim: Display,
        LastDelim: Display,
    {
        ConjunctiveJoin::new_cell(self.iter.take(), delim, last_delim)
    }

    fn oxford_join_custom<Delim, ExactlyTwo, Final>(
        self,
        first_n: Delim,
        exactly_two_conjunction: ExactlyTwo,
        final_delim_conjunction: Final,
    ) -> OxfordJoin<Self::Iter, Delim, ExactlyTwo, Final>
    where
        Self: Sized,
        Delim: Display,
        ExactlyTwo: Display,
        Final: Display,
    {
        OxfordJoin::new_cell(
            self.iter.take(),
            first_n,
            exactly_two_conjunction,
            final_delim_conjunction,
        )
    }

    fn format_each_with<F>(
        self,
        with: F,
    ) -> Literate<impl Iterator<Item = FormatWith<Self::Item, F>>>
    where
        Self: Sized,
        F: Fn(&Self::Item, &mut core::fmt::Formatter) -> core::fmt::Result + Clone,
    {
        self.map_literator(|i| i.format_each_with(with))
    }

    fn capitalize_first(self) -> Literate<impl Iterator<Item: Display>>
    where
        Self: Sized,
        Self::Item: Display,
    {
        self.map_literator(|i| i.capitalize_first())
    }

    fn prefix_each_with<F>(
        self,
        with: F,
    ) -> Literate<impl Iterator<Item = PrefixWith<Self::Item, F>>>
    where
        F: Fn(&Self::Item, &mut core::fmt::Formatter) -> core::fmt::Result + Clone,
        Self: Sized,
    {
        self.map_literator(|i| i.prefix_each_with(with))
    }

    fn suffix_each_with<F>(
        self,
        with: F,
    ) -> Literate<impl Iterator<Item = SuffixWith<Self::Item, F>>>
    where
        F: Fn(&Self::Item, &mut core::fmt::Formatter) -> core::fmt::Result + Clone,
        Self: Sized,
    {
        self.map_literator(|i| i.suffix_each_with(with))
    }

    fn prefix_each<P>(self, prefix: P) -> Literate<impl Iterator<Item = Prefix<Self::Item, P>>>
    where
        P: Display + Clone,
        Self: Sized,
    {
        self.map_literator(|i| i.prefix_each(prefix))
    }

    fn suffix_each<S>(self, suffix: S) -> Literate<impl Iterator<Item = Suffix<Self::Item, S>>>
    where
        S: Display + Clone,
        Self: Sized,
    {
        self.map_literator(|i| i.suffix_each(suffix))
    }

    fn surround_each<P, S>(
        self,
        prefix: P,
        suffix: S,
    ) -> Literate<impl Iterator<Item = Surround<Self::Item, P, S>>>
    where
        P: Display + Clone,
        S: Display + Clone,
        Self: Sized,
    {
        self.map_literator(|i| i.surround_each(prefix, suffix))
    }

    fn indent_each_custom<P, S>(
        self,
        level: usize,
        indentation: P,
        newline: S,
    ) -> Literate<impl Iterator<Item = Surround<Self::Item, Repeat<P>, S>>>
    where
        P: Display + Clone,
        S: Display + Clone,
        Self: Sized,
    {
        self.map_literator(|i| i.indent_each_custom(level, indentation, newline))
    }

    fn indented_block_custom<Open, Close, Indentation, Newline>(
        self,
        open: Open,
        close: Close,
        inner_level: usize,
        indentation: Indentation,
        newline: Newline,
    ) -> IndentedBlock<Self::Iter, Open, Close, Indentation, Newline>
    where
        Self: Sized,
        Open: Display,
        Close: Display,
        Indentation: Display,
        Newline: Display,
    {
        IndentedBlock::new_cell(
            self.iter.take(),
            open,
            close,
            indentation,
            newline,
            inner_level,
        )
    }

    fn inline_block<P, D, S>(self, open: P, delim: D, close: S) -> InlineBlock<Self::Iter, P, D, S>
    where
        Self: Sized,
        P: Display,
        D: Display,
        S: Display,
    {
        InlineBlock::new_cell(self.iter.take(), open, delim, close)
    }
}