1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use ::std::{
    cell::RefCell,
    fmt,
};

/// `separator.join(iterable)` method on anything [`Display`]able.
///
/// Note that the separator goes in first argument position (before the dot),
/// following Python's style.
///
/// The difference with stdlib's [`.join()`] is that it can take a **lazy**
/// sequence of [`Display`]ables, such as [`Itertools::join()`],
/// while also returning a [lazy struct] that, when [`Display`]ed, shall write
/// each item separated by `self`.
///
/// # Example
///
/// ```rust,edition2018
/// use ::join_lazy_fmt::*;
///
/// let sequence = format!("[{}]", ", ".join(0 .. 5));
/// assert_eq!(sequence, "[0, 1, 2, 3, 4]");
///
/// // Since `.join()` is lazy, this does not compute an infinite string.
/// let _ = ", ".join(0 ..);
///
/// const N: usize = 6;
/// let line = format!("+-{}-+", "-+-".join((1 .. N).map(|_| "---")));
/// // And the following allocates only one `String`:
/// let matrix = format!(
///     "{line}\n{body}\n{line}\n",
///     line=line,
///     body="\n".join(
///         (1 .. N).map(|i| lazy_format!(
///             "| {row} |",
///             row=" | ".join(
///                 (1 .. N).map(|j| lazy_format!(
///                     "a{i}{j}",
///                     i=i,
///                     j=j,
///                 ))
///             ),
///         ))
///     ),
/// );
/// assert_eq!(matrix, "\
/// +-----+-----+-----+-----+-----+
/// | a11 | a12 | a13 | a14 | a15 |
/// | a21 | a22 | a23 | a24 | a25 |
/// | a31 | a32 | a33 | a34 | a35 |
/// | a41 | a42 | a43 | a44 | a45 |
/// | a51 | a52 | a53 | a54 | a55 |
/// +-----+-----+-----+-----+-----+
/// ");
/// ```
///
/// [`Display`]: fmt::Display
/// [`.join()`]: https://doc.rust-lang.org/std/slice/trait.SliceConcatExt.html#tymethod.join
/// [`Itertools::join()`]: https://docs.rs/itertools/0.8.0/itertools/trait.Itertools.html#method.format
/// [lazy struct]: DisplayableJoin
pub
trait Join : fmt::Display
{
    #[inline]
    fn join <Iterable> (
        self: &'_ Self,
        iterable: Iterable,
    ) -> DisplayableJoin<'_, Self, Iterable::IntoIter>
    where
        Iterable       : IntoIterator,
        Iterable::Item : fmt::Display,
    {
        DisplayableJoin {
            separator: self,
            iterator: RefCell::new(iterable.into_iter()),
        }
    }
}

impl<Separator : fmt::Display + ?Sized> Join for Separator {}

/// The return value of [`Join::join`],
/// which is lazily [`Display`]able.
///
/// This means that it can be easily instanciated into a [`String`]
/// by calling [`.to_string()`][`::std::string::ToString`] on it,
/// while remaining lazy by default for a performant combination with nested
/// [`Join::join`].
///
/// # Can only be `Display`ed once!
///
/// This is to avoid having to memoize the iterator values from [`Join::join`].
///
/// So, if you need to [`Display`] it multiple times, you have no other choice
/// but to instanciate it into a [`String`] and [`Display`] that instead
/// (_i.e._, no need to implement a non-zero-cost memoizing logic when
/// memoization remains possible through
/// [conversion to a `String`][::std::string::ToString::to_string]).
///
/// [`Display`]: fmt::Display
pub
struct DisplayableJoin<'sep, Sep, Iter>
where
    Sep        : 'sep + fmt::Display + ?Sized,
    Iter       : Iterator,
    Iter::Item : fmt::Display,
{
    separator: &'sep Sep,
    iterator: RefCell<Iter>,
}

impl<'sep, Sep, Iter> fmt::Display for DisplayableJoin<'sep, Sep, Iter>
where
    Sep        : 'sep + fmt::Display + ?Sized,
    Iter       : Iterator,
    Iter::Item : fmt::Display,
{
    /// Note: This should only be called once.
    ///
    /// If you need to call it multiple times,
    /// you should [convert it into a String][::std::string::ToString]
    fn fmt (
        self: &'_ Self,
        stream: &'_ mut fmt::Formatter<'_>,
    ) -> fmt::Result
    {
        let iterator = &mut *self.iterator.borrow_mut();
        if let Some(first_item) = iterator.next() {
            write!(&mut *stream,
                "{item}", item=first_item,
            )?;
            while let Some(next_item) = iterator.next() {
                write!(&mut *stream,
                    "{sep}{item}", sep=self.separator, item=next_item,
                )?;
            }
        }
        Ok(())
    }
}