literator 0.4.0

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

use crate::Literator;

/// 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 {
        Self::new_cell(Some(iter))
    }

    /// Wrap an optional iterator in a `Literate` struct.
    pub const fn new_cell(iter: Option<I>) -> Self {
        Literate {
            iter: Cell::new(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.into_inner() {
            Literate::new(f(iter))
        } else {
            Literate {
                iter: Cell::new(None),
            }
        }
    }

    pub(crate) fn map_literator<F, O>(self, f: F) -> Literate<O>
    where
        F: FnOnce(I) -> Literate<O>,
    {
        if let Some(iter) = self.iter.into_inner() {
            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(iter) = self.iter.take() else {
            return Ok(());
        };
        for item in iter {
            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(iter) = self.iter.take() else {
            return Ok(());
        };
        for item in iter {
            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 into_option_iter(self) -> Option<Self::Iter> {
        self.into_inner()
    }
}