rustdoc/
display.rs

1//! Various utilities for working with [`fmt::Display`] implementations.
2
3use std::fmt::{self, Display, Formatter, FormattingOptions};
4
5pub(crate) trait Joined: IntoIterator {
6    /// Takes an iterator over elements that implement [`Display`], and format them into `f`, separated by `sep`.
7    ///
8    /// This is similar to [`Itertools::format`], but instead of returning an implementation of `Display`,
9    /// it formats directly into a [`Formatter`].
10    ///
11    /// The performance of `joined` is slightly better than `format`, since it doesn't need to use a `Cell` to keep track of whether [`fmt`](Display::fmt)
12    /// was already called (`joined`'s API doesn't allow it be called more than once).
13    ///
14    /// [`Itertools::format`]: https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.format
15    fn joined(&mut self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result;
16}
17
18impl<I, T> Joined for I
19where
20    I: Iterator<Item = T>,
21    T: Display,
22{
23    fn joined(&mut self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result {
24        let Some(first) = self.next() else { return Ok(()) };
25        first.fmt(f)?;
26        for item in self {
27            sep.fmt(f)?;
28            item.fmt(f)?;
29        }
30        Ok(())
31    }
32}
33
34pub(crate) trait MaybeDisplay {
35    /// For a given `Option<T: Display>`, returns a `Display` implementation that will display `t` if `Some(t)`, or nothing if `None`.
36    fn maybe_display(self) -> impl Display;
37}
38
39impl<T: Display> MaybeDisplay for Option<T> {
40    fn maybe_display(self) -> impl Display {
41        fmt::from_fn(move |f| {
42            if let Some(t) = self.as_ref() {
43                t.fmt(f)?;
44            }
45            Ok(())
46        })
47    }
48}
49
50#[derive(Clone, Copy)]
51pub(crate) struct Wrapped<T> {
52    prefix: T,
53    suffix: T,
54}
55
56pub(crate) enum AngleBracket {
57    Open,
58    Close,
59}
60
61impl Display for AngleBracket {
62    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
63        f.write_str(match (self, f.alternate()) {
64            (Self::Open, true) => "<",
65            (Self::Open, false) => "&lt;",
66            (Self::Close, true) => ">",
67            (Self::Close, false) => "&gt;",
68        })
69    }
70}
71
72impl Wrapped<AngleBracket> {
73    pub(crate) fn with_angle_brackets() -> Self {
74        Self { prefix: AngleBracket::Open, suffix: AngleBracket::Close }
75    }
76}
77
78impl Wrapped<char> {
79    pub(crate) fn with_parens() -> Self {
80        Self { prefix: '(', suffix: ')' }
81    }
82
83    pub(crate) fn with_square_brackets() -> Self {
84        Self { prefix: '[', suffix: ']' }
85    }
86}
87
88impl<T: Display> Wrapped<T> {
89    pub(crate) fn with(prefix: T, suffix: T) -> Self {
90        Self { prefix, suffix }
91    }
92
93    pub(crate) fn when(self, if_: bool) -> Wrapped<impl Display> {
94        Wrapped {
95            prefix: if_.then_some(self.prefix).maybe_display(),
96            suffix: if_.then_some(self.suffix).maybe_display(),
97        }
98    }
99
100    pub(crate) fn wrap_fn(
101        self,
102        content: impl Fn(&mut Formatter<'_>) -> fmt::Result,
103    ) -> impl Display {
104        fmt::from_fn(move |f| {
105            self.prefix.fmt(f)?;
106            content(f)?;
107            self.suffix.fmt(f)
108        })
109    }
110
111    pub(crate) fn wrap<C: Display>(self, content: C) -> impl Display {
112        self.wrap_fn(move |f| content.fmt(f))
113    }
114}
115
116#[derive(Clone, Copy)]
117pub(crate) struct WithOpts {
118    opts: FormattingOptions,
119}
120
121impl WithOpts {
122    pub(crate) fn from(f: &Formatter<'_>) -> Self {
123        Self { opts: f.options() }
124    }
125
126    pub(crate) fn display(self, t: impl Display) -> impl Display {
127        fmt::from_fn(move |f| {
128            let mut f = f.with_options(self.opts);
129            t.fmt(&mut f)
130        })
131    }
132}