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}