pretty_trait/
lib.rs

1//! `pretty-trait` is a simple trait-based library for producing pretty debug output.  It is
2//! intended to make it easy to render large tree-like structures (such as program syntax trees) in
3//! such a way that long items are broken across multiple lines and indented.
4//!
5//! The core feature of this crate is the [`Pretty`] trait, which represents types that can be
6//! pretty-printed.  This crate provides a number of built-in types implementing `Pretty`, which be
7//! combined to implement a wide variety of formatting and layout strategies.  For many purposes,
8//! you will not need to implement `Pretty` for your own types, but can instead convert your type
9//! into a structure composed out of these built-in types.
10//!
11//! # Examples
12//!
13//! Converting a custom type to built-in `Pretty` types:
14//!
15//! ```
16//! use pretty_trait::{Pretty, JoinExt, Group, Indent, Sep, delimited, Conditional, to_string, block};
17//!
18//! enum NestList {
19//!     Atom(i32),
20//!     List(Vec<NestList>),
21//! }
22//!
23//! fn to_pretty(nest_list: &NestList) -> Box<Pretty> {
24//!     match nest_list {
25//!         &NestList::Atom(val) => Box::new(val.to_string()),
26//!         &NestList::List(ref children) => {
27//!             Box::new(Group::new(
28//!                 "["
29//!                     .join(block(
30//!                         delimited(&",".join(Sep(1)), children.iter().map(to_pretty))
31//!                             .join(Conditional::OnlyBroken(",")),
32//!                     )).join("]"),
33//!             ))
34//!         }
35//!     }
36//! }
37//!
38//! let max_line = Some(40);
39//! let tab_size = 4;
40//!
41//! let small_list = NestList::List(vec![NestList::Atom(1), NestList::Atom(2), NestList::Atom(3)]);
42//! assert_eq!(to_string(&to_pretty(&small_list), max_line, tab_size), "[1, 2, 3]");
43//!
44//! let large_list = NestList::List(vec![
45//!     NestList::List(vec![
46//!         NestList::Atom(1),
47//!         NestList::Atom(2),
48//!         NestList::Atom(3),
49//!         NestList::Atom(4),
50//!         NestList::Atom(5),
51//!     ]),
52//!     NestList::List(vec![
53//!         NestList::Atom(6),
54//!         NestList::Atom(7),
55//!         NestList::Atom(8),
56//!         NestList::Atom(9),
57//!         NestList::Atom(10),
58//!     ]),
59//!     NestList::List(vec![
60//!         NestList::List(vec![NestList::Atom(11), NestList::Atom(12), NestList::Atom(13)]),
61//!         NestList::List(vec![NestList::Atom(14), NestList::Atom(15), NestList::Atom(16)]),
62//!     ]),
63//! ]);
64//! let expected = "\
65//! [
66//!     [1, 2, 3, 4, 5],
67//!     [6, 7, 8, 9, 10],
68//!     [[11, 12, 13], [14, 15, 16]],
69//! ]";
70//! assert_eq!(to_string(&to_pretty(&large_list), max_line, tab_size), expected);
71//! ```
72//!
73//! [`Pretty`]: trait.Pretty.html
74
75use std::io;
76use std::ops::{Add, Mul, Deref};
77use std::rc::Rc;
78
79/// Represents the number of visual columns a value would take up if it were displayed on one line,
80/// unless it is inherently multi-line.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
82pub enum Size {
83    Size(usize),
84    MultiLine,
85}
86
87impl Size {
88    fn exceeds(self, max_line: Option<usize>) -> bool {
89        self >
90            match max_line {
91                Some(max) => Size::Size(max),
92                None => Size::MultiLine,
93            }
94    }
95}
96
97impl Add<Size> for Size {
98    type Output = Size;
99
100    fn add(self, other: Size) -> Size {
101        match (self, other) {
102            (Size::Size(size1), Size::Size(size2)) => Size::Size(size1 + size2),
103            _ => Size::MultiLine,
104        }
105    }
106}
107
108impl Mul<usize> for Size {
109    type Output = Size;
110
111    fn mul(self, other: usize) -> Size {
112        match self {
113            Size::Size(size) => Size::Size(size * other),
114            Size::MultiLine => Size::MultiLine,
115        }
116    }
117}
118
119/// A struct used internally in pretty-printing to store information about the rendering
120/// environment.
121///
122/// You only need to use this struct if you are implementing your own `Pretty` types.  To render an
123/// existing `Pretty` type with custom line length and tab size parameters, use the `max_line` and
124/// `tab_size` arguments of the [`write`] or [`to_string`] functions.
125///
126/// [`write`]: fn.write.html
127/// [`to_string`]: fn.to_string.html
128pub struct Context<'a> {
129    /// The maximum desired line length, or `None` if lines may be of unlimited length.
130    pub max_line: Option<usize>,
131
132    /// The desired number of spaces to use for a single level of indentation.
133    pub tab_size: usize,
134
135    /// The current number of tab stops to be inserted before each new line.
136    pub indent_level: usize,
137
138    /// Whether or not the environment has been broken across multiple lines because its contents
139    /// were too large.
140    pub broken: bool,
141
142    /// The handle to render to.
143    pub writer: &'a mut io::Write,
144}
145
146impl<'a> Context<'a> {
147    fn reborrow<'b>(&'b mut self) -> Context<'b> {
148        Context {
149            max_line: self.max_line,
150            tab_size: self.tab_size,
151            indent_level: self.indent_level,
152            broken: self.broken,
153            writer: &mut self.writer,
154        }
155    }
156}
157
158/// Types which can be pretty-printed.
159///
160/// Strings implement `Pretty`, as do a number of useful built-in composable wrapper types.  As
161/// such, you usually don't need to implement it for your own types, although you can if necessary.
162///
163/// You usually do not need to directly call the methods defined here, unless your are implementing
164/// your own `Pretty` type.  If you just want to render a value to a buffer or an IO handle, use one
165/// of the [`write`], [`println_simple`], or [`to_string`] functions instead.
166///
167/// [`write`]: fn.write.html
168/// [`println_simple`]: fn.println_simple.html
169/// [`to_string`]: fn.to_string.html
170pub trait Pretty {
171    /// Calculate the intrinsic size of this value, if it were to be displayed on a single line.
172    fn size(&self) -> Size;
173
174    /// Render this value in a given context.
175    fn pretty_write(&self, context: Context) -> io::Result<()>;
176}
177
178impl<'a, T: Pretty + ?Sized> Pretty for &'a T {
179    fn size(&self) -> Size {
180        (*self).size()
181    }
182
183    fn pretty_write(&self, context: Context) -> io::Result<()> {
184        (*self).pretty_write(context)
185    }
186}
187
188impl<'a, T: Pretty + ?Sized> Pretty for &'a mut T {
189    fn size(&self) -> Size {
190        (**self).size()
191    }
192
193    fn pretty_write(&self, context: Context) -> io::Result<()> {
194        (**self).pretty_write(context)
195    }
196}
197
198impl<'a, T: Pretty + ?Sized> Pretty for Box<T> {
199    fn size(&self) -> Size {
200        self.deref().size()
201    }
202
203    fn pretty_write(&self, context: Context) -> io::Result<()> {
204        self.deref().pretty_write(context)
205    }
206}
207
208impl<'a, T: Pretty + ?Sized> Pretty for Rc<T> {
209    fn size(&self) -> Size {
210        self.deref().size()
211    }
212
213    fn pretty_write(&self, context: Context) -> io::Result<()> {
214        self.deref().pretty_write(context)
215    }
216}
217
218impl<'a> Pretty for &'a str {
219    fn size(&self) -> Size {
220        Size::Size(self.chars().count())
221    }
222
223    fn pretty_write(&self, context: Context) -> io::Result<()> {
224        write!(context.writer, "{}", self)
225    }
226}
227
228impl Pretty for String {
229    fn size(&self) -> Size {
230        Size::Size(self.chars().count())
231    }
232
233    fn pretty_write(&self, context: Context) -> io::Result<()> {
234        write!(context.writer, "{}", self)
235    }
236}
237
238/// A wrapper which groups its contents so they will fit onto one line if possible, even if their
239/// environment has been broken across multiple lines.
240///
241/// # Examples
242///
243/// ```
244/// use pretty_trait::{JoinExt, Group, Sep, to_string};
245///
246/// let max_line = Some(10);
247/// let tab_size = 4;
248///
249/// let expected_ungrouped = "\
250/// hello
251/// ,
252/// world
253/// !";
254///
255/// assert_eq!(
256///     to_string(
257///         &"hello"
258///             .join(Sep(0))
259///             .join(",")
260///             .join(Sep(1))
261///             .join("world")
262///             .join(Sep(0))
263///             .join("!"),
264///         max_line,
265///         tab_size,
266///     ),
267///     expected_ungrouped
268/// );
269///
270/// let expected_grouped = "\
271/// hello,
272/// world!";
273///
274/// assert_eq!(
275///     to_string(
276///         &Group::new("hello".join(Sep(0)).join(","))
277///             .join(Sep(1))
278///             .join(Group::new("world".join(Sep(0)).join("!"))),
279///         max_line,
280///         tab_size,
281///     ),
282///     expected_grouped,
283/// );
284/// ```
285#[derive(Clone, Copy, Debug)]
286pub struct Group<T> {
287    size: Size,
288    content: T,
289}
290
291impl<T: Pretty> Group<T> {
292    pub fn new(content: T) -> Self {
293        Group {
294            size: content.size(),
295            content,
296        }
297    }
298}
299
300impl<T: Pretty> Pretty for Group<T> {
301    fn size(&self) -> Size {
302        self.size
303    }
304
305    fn pretty_write(&self, mut context: Context) -> io::Result<()> {
306        let indented_size = self.size + Size::Size(context.indent_level * context.tab_size);
307        context.broken = indented_size.exceeds(context.max_line);
308        self.content.pretty_write(context)
309    }
310}
311
312/// A whitespace separator, rendered as a space if unbroken or a newline if broken.
313///
314/// The most common uses of `Sep` are `Sep(1)`, which renders as a single space or a newline, and
315/// `Sep(0)`, which introduces a point where a newline will be inserted if the content is broken.
316///
317/// # Examples
318///
319/// Breaking into multiple lines:
320///
321/// ```
322/// use pretty_trait::{JoinExt, Sep, to_string};
323///
324/// let max_line = Some(10);
325/// let tab_size = 4;
326///
327/// // Exceeding the line length without a separator:
328/// assert_eq!(to_string(&"hello".join("world!"), max_line, tab_size), "helloworld!");
329///
330/// let expected_broken = "\
331/// hello
332/// world!";
333///
334/// assert_eq!(
335///     to_string(&"hello".join(Sep(0)).join("world!"), max_line, tab_size),
336///     expected_broken
337/// );
338///
339/// assert_eq!(
340///     to_string(&"hello".join(Sep(1)).join("world!"), max_line, tab_size),
341///     expected_broken
342/// );
343/// ```
344///
345/// Introducing spaces on a single line:
346///
347/// ```
348/// # use pretty_trait::{JoinExt, Sep, to_string};
349/// # let tab_size = 4;
350/// #
351/// assert_eq!(
352///     to_string(&"hello".join(Sep(1)).join("world!"), None, tab_size),
353///     "hello world!"
354/// );
355///
356/// assert_eq!(
357///     to_string(&"hello".join(Sep(0)).join("world!"), None, tab_size),
358///     "helloworld!"
359/// );
360/// ```
361#[derive(Clone, Copy, Debug)]
362pub struct Sep(pub usize);
363
364impl Pretty for Sep {
365    fn size(&self) -> Size {
366        Size::Size(self.0)
367    }
368
369    fn pretty_write(&self, context: Context) -> io::Result<()> {
370        if context.broken {
371            writeln!(context.writer, "")?;
372            for _ in 0..(context.tab_size * context.indent_level) {
373                write!(context.writer, " ")?;
374            }
375        } else {
376            for _ in 0..self.0 {
377                write!(context.writer, " ")?;
378            }
379        }
380        Ok(())
381    }
382}
383
384/// An unconditional newline.
385///
386/// Always causes its environment to break.
387///
388/// # Examples
389///
390/// Basic usage:
391///
392/// ```
393/// use pretty_trait::{JoinExt, Newline, to_string};
394///
395/// let expected = "\
396/// hello
397/// world";
398///
399/// assert_eq!(to_string(&"hello".join(Newline).join("world"), None, 4), expected);
400/// ```
401#[derive(Clone, Copy, Debug)]
402pub struct Newline;
403
404impl Pretty for Newline {
405    fn size(&self) -> Size {
406        Size::MultiLine
407    }
408
409    fn pretty_write(&self, context: Context) -> io::Result<()> {
410        writeln!(context.writer, "")?;
411        for _ in 0..(context.tab_size * context.indent_level) {
412            write!(context.writer, " ")?;
413        }
414        Ok(())
415    }
416}
417
418/// A wrapper which indents any newlines inside its contents.
419///
420/// # Examples
421///
422/// Basic usage:
423///
424/// ```
425/// use pretty_trait::{JoinExt, Sep, Indent, to_string};
426///
427/// let max_line = Some(20);
428/// let tab_size = 4;
429///
430/// let expected = "\
431/// (
432///     lorem
433///     ipsum
434///     dolor
435///     sit
436///     amet
437/// )";
438///
439/// assert_eq!(
440///     to_string(
441///         &"(".join(Indent(
442///             Sep(0)
443///                 .join("lorem")
444///                 .join(Sep(1))
445///                 .join("ipsum")
446///                 .join(Sep(1))
447///                 .join("dolor")
448///                 .join(Sep(1))
449///                 .join("sit")
450///                 .join(Sep(1))
451///                 .join("amet")
452///         )).join(Sep(0)).join(")"),
453///         max_line,
454///         tab_size,
455///     ),
456///     expected
457/// );
458/// ```
459///
460/// # Caution
461///
462/// To indent a block enclosed in paired delimiters like brackets, care must be taken to ensure that
463/// the first line of the content *is* indented, and that the closing delimiter *is not* indented
464/// along with its contents.  To ensure this, the newline after the opening delimiter should occur
465/// *inside* the `Indent` block, and the newline before the closing delimiter should occur *outside*
466/// the `Indent` block, as in the example above.  The [`block`] function implements this pattern.
467///
468/// [`block`]: fn.block.html
469#[derive(Clone, Copy, Debug)]
470pub struct Indent<T>(pub T);
471
472impl<T: Pretty> Pretty for Indent<T> {
473    fn size(&self) -> Size {
474        self.0.size()
475    }
476
477    fn pretty_write(&self, mut context: Context) -> io::Result<()> {
478        context.indent_level += 1;
479        self.0.pretty_write(context)
480    }
481}
482
483/// A wrapper which concatenates two pretty-printable values.
484///
485/// This struct is created by the [`join`] method from the `JoinExt` trait.  See its documentation
486/// for more.
487///
488/// [`join`]: trait.JoinExt.html#method.join
489#[derive(Clone, Copy, Debug)]
490pub struct Join<T, U>(pub T, pub U);
491
492impl<T: Pretty, U: Pretty> Pretty for Join<T, U> {
493    fn size(&self) -> Size {
494        self.0.size() + self.1.size()
495    }
496
497    fn pretty_write(&self, mut context: Context) -> io::Result<()> {
498        self.0.pretty_write(context.reborrow())?;
499        self.1.pretty_write(context)?;
500        Ok(())
501    }
502}
503
504/// Allows `join` to be called on any `Pretty` type.
505///
506/// This trait is automatically implemented for all `Pretty` types.  It should never be implemented
507/// manually.
508pub trait JoinExt: Sized {
509    /// Concatenate two pretty-printable values.  This directly displays one after the other, with
510    /// no separation or line breaks.  For separation, use the [`Sep`] type.
511    ///
512    /// # Examples
513    ///
514    /// Basic usage:
515    ///
516    /// ```
517    /// use pretty_trait::{JoinExt, to_string};
518    ///
519    /// let max_line = Some(10);
520    /// let tab_size = 4;
521    ///
522    /// // Exceeds maximum line length, but does not break because there is no break-point:
523    /// assert_eq!(
524    ///     to_string(&"hello".join("world!"), max_line, tab_size),
525    ///     "helloworld!"
526    /// );
527    /// ```
528    ///
529    /// [`Sep`]: struct.Sep.html
530    fn join<U>(self, other: U) -> Join<Self, U>;
531}
532
533impl<T: Pretty> JoinExt for T {
534    fn join<U>(self, other: U) -> Join<Self, U> {
535        Join(self, other)
536    }
537}
538
539/// A wrapper that concatenates an arbitrary sequence of pretty-printable values.
540///
541/// # Examples
542///
543/// Basic usage:
544///
545/// ```
546/// use pretty_trait::{JoinExt, Sep, Seq, to_string};
547///
548/// let max_line = Some(10);
549/// let tab_size = 4;
550///
551/// let expected = "\
552/// lorem
553/// ipsum
554/// dolor
555/// sit
556/// amet";
557///
558/// assert_eq!(
559///     to_string(
560///         &Seq(vec![
561///             "lorem".join(Some(Sep(1))),
562///             "ipsum".join(Some(Sep(1))),
563///             "dolor".join(Some(Sep(1))),
564///             "sit".join(Some(Sep(1))),
565///             "amet".join(None),
566///         ]),
567///         max_line,
568///         tab_size,
569///     ),
570///     expected
571/// );
572/// ```
573///
574/// # Note
575///
576/// Because a `Seq` is just a thin wrapper around a `Vec`, all of its items must be of the same
577/// type.  When working with combinators like `join` this can sometimes be confusing. For example,
578/// the following code will not compile because the final element of the `Vec` does not have the
579/// same type as the others:
580///
581/// ```compile_fail
582/// # use pretty_trait::{JoinExt, Seq, Sep};
583/// Seq(vec![
584///     "lorem".join(Sep(1)),
585///     "ipsum".join(Sep(1)),
586///     "dolor".join(Sep(1)),
587///     "sit".join(Sep(1)),
588///     "amet",
589/// ]);
590/// ```
591#[derive(Clone, Debug)]
592pub struct Seq<T>(pub Vec<T>);
593
594impl<T: Pretty> Pretty for Seq<T> {
595    fn size(&self) -> Size {
596        self.0.iter().fold(
597            Size::Size(0),
598            |total, item| total + item.size(),
599        )
600    }
601
602    fn pretty_write(&self, mut context: Context) -> io::Result<()> {
603        for item in &self.0 {
604            item.pretty_write(context.reborrow())?;
605        }
606        Ok(())
607    }
608}
609
610/// Render a pretty-printable value to an arbitrary `io::Write` handle.
611///
612/// This is the most general way to render a `Pretty` type.
613pub fn write<T: Pretty>(
614    writer: &mut io::Write,
615    content: &T,
616    max_line: Option<usize>,
617    tab_size: usize,
618) -> io::Result<()> {
619    let size = content.size();
620    let context = Context {
621        max_line,
622        tab_size,
623        indent_level: 0,
624        broken: size.exceeds(max_line),
625        writer,
626    };
627    content.pretty_write(context)
628}
629
630/// Render a pretty-printable value to an owned string and return it.
631///
632/// If you just want to write a value to standard output, you probably want one of the more
633/// efficient [`println_simple`] or [`write`] functions instead.
634///
635/// # Panics
636///
637/// Because `Pretty` is defined in terms of writing to an `io::Write` handle, not a string, there is
638/// no guarantee that rendering a `Pretty` type will produce valid UTF-8.  None of the built-in
639/// types in the `pretty-trait` crate will produce invalid UTF-8, but if a custom `Pretty` type
640/// generates invalid UTF-8 then this function will panic.
641///
642/// [`println_simple`]: fn.println_simple.html
643/// [`write`]: fn.write.html
644pub fn to_string<T: Pretty>(content: &T, max_line: Option<usize>, tab_size: usize) -> String {
645    let mut result = Vec::new();
646    write(&mut result, content, max_line, tab_size).expect("Writing to a string should not fail");
647    String::from_utf8(result).expect("Invalid UTF8")
648}
649
650/// Conveniently render a pretty-printable value to standard output.
651///
652/// This function uses a default maximum line length of 80 characters, and a tab size of 2 spaces.
653pub fn println_simple<T: Pretty>(content: &T) {
654    write(&mut io::stdout(), content, Some(80), 2).unwrap();
655    println!("");
656}
657
658/// A wrapper which decides whether or not to render its contents based on the breaking mode of the
659/// environment.
660///
661/// # Examples
662///
663/// Adding a trailing comma only when broken:
664///
665/// ```
666/// use pretty_trait::{JoinExt, Sep, Conditional, to_string};
667///
668/// let max_line = Some(10);
669/// let tab_size = 4;
670///
671/// let to_render = "lorem,"
672///     .join(Sep(1))
673///     .join("ipsum,")
674///     .join(Sep(1))
675///     .join("dolor,")
676///     .join(Sep(1))
677///     .join("sit,")
678///     .join(Sep(1))
679///     .join("amet")
680///     .join(Conditional::OnlyBroken(","));
681///
682/// // Trailing comma when broken across multiple lines:
683///
684/// let expected_broken = "\
685/// lorem,
686/// ipsum,
687/// dolor,
688/// sit,
689/// amet,";
690///
691/// assert_eq!(to_string(&to_render, max_line, tab_size), expected_broken);
692///
693/// // No trailing comma when rendered on a single line:
694///
695/// assert_eq!(
696///     to_string(&to_render, None, tab_size),
697///     "lorem, ipsum, dolor, sit, amet"
698/// );
699/// ```
700#[derive(Clone, Copy, Debug)]
701pub enum Conditional<T> {
702    /// Render the wrapped value under all circumstances
703    Always(T),
704
705    /// Render the wrapped value only when it appears in a multi-line context
706    OnlyBroken(T),
707
708    /// Render the wrapped value only when it appears in a single-line context
709    OnlyUnbroken(T),
710}
711
712impl<T: Pretty> Pretty for Conditional<T> {
713    fn size(&self) -> Size {
714        Size::Size(0)
715    }
716
717    fn pretty_write(&self, context: Context) -> io::Result<()> {
718        match (self, context.broken) {
719            (&Conditional::Always(ref inner), _) |
720            (&Conditional::OnlyBroken(ref inner), true) |
721            (&Conditional::OnlyUnbroken(ref inner), false) => inner.pretty_write(context),
722            _ => Ok(()),
723        }
724    }
725}
726
727/// An `Option` will render its contents if it is `Some`, or an empty string if it is `None`.
728///
729/// This is useful when you need multiple pretty values to have the same type, even though they are
730/// not all of exactly the same form when rendered.
731///
732/// # Examples
733///
734/// Basic usage:
735///
736/// ```
737/// use pretty_trait::{JoinExt, Seq, Sep, to_string};
738///
739/// let tab_size = 4;
740///
741/// assert_eq!(
742///     to_string(
743///         &Seq(vec![
744///             "lorem".join(Some(",".join(Sep(1)))),
745///             "ipsum".join(Some(",".join(Sep(1)))),
746///             "dolor".join(Some(",".join(Sep(1)))),
747///             "sit".join(Some(",".join(Sep(1)))),
748///             "amet".join(None),
749///         ]),
750///         None,
751///         tab_size,
752///     ),
753///     "lorem, ipsum, dolor, sit, amet"
754/// );
755/// ```
756///
757/// If the above example were modified so that it did not use `Option`s, it would not compile
758/// because the last item in the `Seq` would have a mismatched type:
759///
760/// ```compile_fail
761/// # use pretty_trait::{JoinExt, Seq, Sep};
762/// Seq(vec![
763///     "lorem".join(",".join(Some(Sep(1)))),
764///     "ipsum".join(",".join(Some(Sep(1)))),
765///     "dolor".join(",".join(Some(Sep(1)))),
766///     "sit".join(",".join(Some(Sep(1)))),
767///     "amet",
768/// ]);
769/// ```
770impl<T: Pretty> Pretty for Option<T> {
771    fn size(&self) -> Size {
772        match self {
773            &Some(ref inner) => inner.size(),
774            &None => Size::Size(0),
775        }
776    }
777
778    fn pretty_write(&self, context: Context) -> io::Result<()> {
779        match self {
780            &Some(ref inner) => inner.pretty_write(context),
781            &None => Ok(()),
782        }
783    }
784}
785
786/// Separate a sequence of pretty-printable values by a delimiter.
787///
788/// The delimiter is not included on the last item.
789///
790/// # Examples
791///
792/// Basic usage:
793///
794/// ```
795/// use pretty_trait::{JoinExt, Sep, delimited, to_string};
796///
797/// assert_eq!(
798///     to_string(&delimited(&",".join(Sep(1)), &["lorem", "ipsum", "dolor"]), None, 4),
799///     "lorem, ipsum, dolor"
800/// );
801/// ```
802pub fn delimited<Delim, Item, It>(delim: &Delim, it: It) -> Seq<Join<Item, Option<Delim>>>
803where
804    Delim: Pretty + Clone,
805    Item: Pretty,
806    It: IntoIterator<Item = Item>,
807{
808    let mut iter = it.into_iter().peekable();
809    let mut results = Vec::new();
810    while let Some(item) = iter.next() {
811        let cond_delim = if iter.peek().is_some() {
812            Some(delim.clone())
813        } else {
814            None
815        };
816        results.push(item.join(cond_delim));
817    }
818    Seq(results)
819}
820
821/// Wrap a pretty-printable value so that it will display as an indented block when broken across
822/// multiple lines.
823///
824/// When displayed on a single line, `block` has no effect.
825///
826/// When displayed on multiple lines, `block` inserts appropriate newlines before and after its
827/// content.
828///
829/// `block(x)` is equivalent to `Indent(Sep(0).join(x)).join(Sep(0))`.
830///
831/// # Examples
832///
833/// Basic usage:
834///
835/// ```
836/// use pretty_trait::{JoinExt, Sep, block, to_string};
837///
838/// let max_line = Some(10);
839/// let tab_size = 2;
840///
841/// let expected_broken = "\
842/// (
843///   hello
844///   world
845/// )";
846///
847/// let blocked = "(".join(block("hello".join(Sep(1)).join("world"))).join(")");
848///
849/// assert_eq!(
850///     to_string(&blocked, max_line, tab_size),
851///     expected_broken,
852/// );
853///
854/// // When displayed on one line, block has no effect:
855///
856/// let expected_unbroken = "(hello world)";
857///
858/// assert_eq!(
859///     to_string(&blocked, None, tab_size),
860///     expected_unbroken,
861/// );
862/// ```
863pub fn block<T: Pretty>(content: T) -> Join<Indent<Join<Sep, T>>, Sep> {
864    Indent(Sep(0).join(content)).join(Sep(0))
865}