nu_ansi_term/
display.rs

1use crate::ansi::RESET;
2use crate::difference::Difference;
3use crate::style::{Color, Style};
4use crate::write::AnyWrite;
5use std::borrow::Cow;
6use std::fmt;
7use std::io;
8
9/// An `AnsiGenericString` includes a generic string type and a `Style` to
10/// display that string.  `AnsiString` and `AnsiByteString` are aliases for
11/// this type on `str` and `\[u8]`, respectively.
12#[derive(PartialEq, Debug)]
13pub struct AnsiGenericString<'a, S: 'a + ToOwned + ?Sized>
14where
15    <S as ToOwned>::Owned: fmt::Debug,
16{
17    pub(crate) style: Style,
18    pub(crate) string: Cow<'a, S>,
19}
20
21/// Cloning an `AnsiGenericString` will clone its underlying string.
22///
23/// # Examples
24///
25/// ```
26/// use nu_ansi_term::AnsiString;
27///
28/// let plain_string = AnsiString::from("a plain string");
29/// let clone_string = plain_string.clone();
30/// assert_eq!(clone_string, plain_string);
31/// ```
32impl<'a, S: 'a + ToOwned + ?Sized> Clone for AnsiGenericString<'a, S>
33where
34    <S as ToOwned>::Owned: fmt::Debug,
35{
36    fn clone(&self) -> AnsiGenericString<'a, S> {
37        AnsiGenericString {
38            style: self.style,
39            string: self.string.clone(),
40        }
41    }
42}
43
44// You might think that the hand-written Clone impl above is the same as the
45// one that gets generated with #[derive]. But it’s not *quite* the same!
46//
47// `str` is not Clone, and the derived Clone implementation puts a Clone
48// constraint on the S type parameter (generated using --pretty=expanded):
49//
50//                  ↓_________________↓
51//     impl <'a, S: ::std::clone::Clone + 'a + ToOwned + ?Sized> ::std::clone::Clone
52//     for ANSIGenericString<'a, S> where
53//     <S as ToOwned>::Owned: fmt::Debug { ... }
54//
55// This resulted in compile errors when you tried to derive Clone on a type
56// that used it:
57//
58//     #[derive(PartialEq, Debug, Clone, Default)]
59//     pub struct TextCellContents(Vec<AnsiString<'static>>);
60//                                 ^^^^^^^^^^^^^^^^^^^^^^^^^
61//     error[E0277]: the trait `std::clone::Clone` is not implemented for `str`
62//
63// The hand-written impl above can ignore that constraint and still compile.
64
65/// An ANSI String is a string coupled with the `Style` to display it
66/// in a terminal.
67///
68/// Although not technically a string itself, it can be turned into
69/// one with the `to_string` method.
70///
71/// # Examples
72///
73/// ```
74/// use nu_ansi_term::AnsiString;
75/// use nu_ansi_term::Color::Red;
76///
77/// let red_string = Red.paint("a red string");
78/// println!("{}", red_string);
79/// ```
80///
81/// ```
82/// use nu_ansi_term::AnsiString;
83///
84/// let plain_string = AnsiString::from("a plain string");
85/// ```
86pub type AnsiString<'a> = AnsiGenericString<'a, str>;
87
88/// An `AnsiByteString` represents a formatted series of bytes.  Use
89/// `AnsiByteString` when styling text with an unknown encoding.
90pub type AnsiByteString<'a> = AnsiGenericString<'a, [u8]>;
91
92impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for AnsiGenericString<'a, S>
93where
94    I: Into<Cow<'a, S>>,
95    <S as ToOwned>::Owned: fmt::Debug,
96{
97    fn from(input: I) -> AnsiGenericString<'a, S> {
98        AnsiGenericString {
99            string: input.into(),
100            style: Style::default(),
101        }
102    }
103}
104
105impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
106where
107    <S as ToOwned>::Owned: fmt::Debug,
108{
109    /// Directly access the style
110    pub fn style_ref(&self) -> &Style {
111        &self.style
112    }
113
114    /// Directly access the style mutably
115    pub fn style_ref_mut(&mut self) -> &mut Style {
116        &mut self.style
117    }
118}
119
120/// A set of `AnsiGenericStrings`s collected together, in order to be
121/// written with a minimum of control characters.
122#[derive(Debug, PartialEq)]
123pub struct AnsiGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [AnsiGenericString<'a, S>])
124where
125    <S as ToOwned>::Owned: fmt::Debug,
126    S: PartialEq;
127
128/// A set of `AnsiString`s collected together, in order to be written with a
129/// minimum of control characters.
130pub type AnsiStrings<'a> = AnsiGenericStrings<'a, str>;
131
132/// A function to construct an `AnsiStrings` instance.
133#[allow(non_snake_case)]
134pub fn AnsiStrings<'a>(arg: &'a [AnsiString<'a>]) -> AnsiStrings<'a> {
135    AnsiGenericStrings(arg)
136}
137
138/// A set of `AnsiByteString`s collected together, in order to be
139/// written with a minimum of control characters.
140pub type AnsiByteStrings<'a> = AnsiGenericStrings<'a, [u8]>;
141
142/// A function to construct an `AnsiByteStrings` instance.
143#[allow(non_snake_case)]
144pub fn AnsiByteStrings<'a>(arg: &'a [AnsiByteString<'a>]) -> AnsiByteStrings<'a> {
145    AnsiGenericStrings(arg)
146}
147
148// ---- paint functions ----
149
150impl Style {
151    /// Paints the given text with this color, returning an ANSI string.
152    #[must_use]
153    pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
154    where
155        I: Into<Cow<'a, S>>,
156        <S as ToOwned>::Owned: fmt::Debug,
157    {
158        AnsiGenericString {
159            string: input.into(),
160            style: self,
161        }
162    }
163}
164
165impl Color {
166    /// Paints the given text with this color, returning an ANSI string.
167    /// This is a short-cut so you don’t have to use `Blue.normal()` just
168    /// to get blue text.
169    ///
170    /// ```
171    /// use nu_ansi_term::Color::Blue;
172    /// println!("{}", Blue.paint("da ba dee"));
173    /// ```
174    #[must_use]
175    pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
176    where
177        I: Into<Cow<'a, S>>,
178        <S as ToOwned>::Owned: fmt::Debug,
179    {
180        AnsiGenericString {
181            string: input.into(),
182            style: self.normal(),
183        }
184    }
185}
186
187// ---- writers for individual ANSI strings ----
188
189impl<'a> fmt::Display for AnsiString<'a> {
190    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191        let w: &mut dyn fmt::Write = f;
192        self.write_to_any(w)
193    }
194}
195
196impl<'a> AnsiByteString<'a> {
197    /// Write an `AnsiByteString` to an `io::Write`.  This writes the escape
198    /// sequences for the associated `Style` around the bytes.
199    pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
200        let w: &mut dyn io::Write = w;
201        self.write_to_any(w)
202    }
203}
204
205impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
206where
207    <S as ToOwned>::Owned: fmt::Debug,
208    &'a S: AsRef<[u8]>,
209{
210    fn write_to_any<W: AnyWrite<Wstr = S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
211        write!(w, "{}", self.style.prefix())?;
212        w.write_str(self.string.as_ref())?;
213        write!(w, "{}", self.style.suffix())
214    }
215}
216
217// ---- writers for combined ANSI strings ----
218
219impl<'a> fmt::Display for AnsiStrings<'a> {
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        let f: &mut dyn fmt::Write = f;
222        self.write_to_any(f)
223    }
224}
225
226impl<'a> AnsiByteStrings<'a> {
227    /// Write `AnsiByteStrings` to an `io::Write`.  This writes the minimal
228    /// escape sequences for the associated `Style`s around each set of
229    /// bytes.
230    pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
231        let w: &mut dyn io::Write = w;
232        self.write_to_any(w)
233    }
234}
235
236impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> AnsiGenericStrings<'a, S>
237where
238    <S as ToOwned>::Owned: fmt::Debug,
239    &'a S: AsRef<[u8]>,
240{
241    fn write_to_any<W: AnyWrite<Wstr = S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
242        use self::Difference::*;
243
244        let first = match self.0.first() {
245            None => return Ok(()),
246            Some(f) => f,
247        };
248
249        write!(w, "{}", first.style.prefix())?;
250        w.write_str(first.string.as_ref())?;
251
252        for window in self.0.windows(2) {
253            match Difference::between(&window[0].style, &window[1].style) {
254                ExtraStyles(style) => write!(w, "{}", style.prefix())?,
255                Reset => write!(w, "{}{}", RESET, window[1].style.prefix())?,
256                Empty => { /* Do nothing! */ }
257            }
258
259            w.write_str(&window[1].string)?;
260        }
261
262        // Write the final reset string after all of the AnsiStrings have been
263        // written, *except* if the last one has no styles, because it would
264        // have already been written by this point.
265        if let Some(last) = self.0.last() {
266            if !last.style.is_plain() {
267                write!(w, "{}", RESET)?;
268            }
269        }
270
271        Ok(())
272    }
273}
274
275// ---- tests ----
276
277#[cfg(test)]
278mod tests {
279    pub use super::super::AnsiStrings;
280    pub use crate::style::Color::*;
281    pub use crate::style::Style;
282
283    #[test]
284    fn no_control_codes_for_plain() {
285        let one = Style::default().paint("one");
286        let two = Style::default().paint("two");
287        let output = AnsiStrings(&[one, two]).to_string();
288        assert_eq!(output, "onetwo");
289    }
290}