ansi_term/
display.rs

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