1 | use std::borrow::Cow; |
2 | use std::fmt; |
3 | use std::io; |
4 | use std::ops::Deref; |
5 | |
6 | use ansi::RESET; |
7 | use difference::Difference; |
8 | use style::{Style, Colour}; |
9 | use 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)] |
16 | pub struct ANSIGenericString<'a, S: 'a + ToOwned + ?Sized> |
17 | where <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 | /// ``` |
34 | impl<'a, S: 'a + ToOwned + ?Sized> Clone for ANSIGenericString<'a, S> |
35 | where <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 | /// ``` |
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 | /// ``` |
89 | pub 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. |
93 | pub type ANSIByteString<'a> = ANSIGenericString<'a, [u8]>; |
94 | |
95 | impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for ANSIGenericString<'a, S> |
96 | where 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 | |
106 | impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S> |
107 | where <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 | impl<'a, S: 'a + ToOwned + ?Sized> Deref for ANSIGenericString<'a, S> |
121 | where <S as ToOwned>::Owned: fmt::Debug { |
122 | type Target = S; |
123 | |
124 | fn deref(&self) -> &S { |
125 | self.string.deref() |
126 | } |
127 | } |
128 | |
129 | |
130 | /// A set of `ANSIGenericString`s collected together, in order to be |
131 | /// written with a minimum of control characters. |
132 | #[derive (Debug, PartialEq)] |
133 | pub struct ANSIGenericStrings<'a, S: 'a + ToOwned + ?Sized> |
134 | (pub &'a [ANSIGenericString<'a, S>]) |
135 | where <S as ToOwned>::Owned: fmt::Debug, S: PartialEq; |
136 | |
137 | /// A set of `ANSIString`s collected together, in order to be written with a |
138 | /// minimum of control characters. |
139 | pub type ANSIStrings<'a> = ANSIGenericStrings<'a, str>; |
140 | |
141 | /// A function to construct an `ANSIStrings` instance. |
142 | #[allow (non_snake_case)] |
143 | pub fn ANSIStrings<'a>(arg: &'a [ANSIString<'a>]) -> ANSIStrings<'a> { |
144 | ANSIGenericStrings(arg) |
145 | } |
146 | |
147 | /// A set of `ANSIByteString`s collected together, in order to be |
148 | /// written with a minimum of control characters. |
149 | pub type ANSIByteStrings<'a> = ANSIGenericStrings<'a, [u8]>; |
150 | |
151 | /// A function to construct an `ANSIByteStrings` instance. |
152 | #[allow (non_snake_case)] |
153 | pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a> { |
154 | ANSIGenericStrings(arg) |
155 | } |
156 | |
157 | |
158 | // ---- paint functions ---- |
159 | |
160 | impl Style { |
161 | |
162 | /// Paints the given text with this colour, returning an ANSI string. |
163 | #[must_use ] |
164 | pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S> |
165 | where I: Into<Cow<'a, S>>, |
166 | <S as ToOwned>::Owned: fmt::Debug { |
167 | ANSIGenericString { |
168 | string: input.into(), |
169 | style: self, |
170 | } |
171 | } |
172 | } |
173 | |
174 | |
175 | impl Colour { |
176 | |
177 | /// Paints the given text with this colour, returning an ANSI string. |
178 | /// This is a short-cut so you don’t have to use `Blue.normal()` just |
179 | /// to get blue text. |
180 | /// |
181 | /// ``` |
182 | /// use ansi_term::Colour::Blue; |
183 | /// println!("{}" , Blue.paint("da ba dee" )); |
184 | /// ``` |
185 | #[must_use ] |
186 | pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S> |
187 | where I: Into<Cow<'a, S>>, |
188 | <S as ToOwned>::Owned: fmt::Debug { |
189 | ANSIGenericString { |
190 | string: input.into(), |
191 | style: self.normal(), |
192 | } |
193 | } |
194 | } |
195 | |
196 | |
197 | // ---- writers for individual ANSI strings ---- |
198 | |
199 | impl<'a> fmt::Display for ANSIString<'a> { |
200 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
201 | let w: &mut fmt::Write = f; |
202 | self.write_to_any(w) |
203 | } |
204 | } |
205 | |
206 | impl<'a> ANSIByteString<'a> { |
207 | /// Write an `ANSIByteString` to an `io::Write`. This writes the escape |
208 | /// sequences for the associated `Style` around the bytes. |
209 | pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> { |
210 | let w: &mut io::Write = w; |
211 | self.write_to_any(w) |
212 | } |
213 | } |
214 | |
215 | impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S> |
216 | where <S as ToOwned>::Owned: fmt::Debug, &'a S: AsRef<[u8]> { |
217 | fn write_to_any<W: AnyWrite<wstr=S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> { |
218 | write!(w, " {}" , self.style.prefix())?; |
219 | w.write_str(self.string.as_ref())?; |
220 | write!(w, " {}" , self.style.suffix()) |
221 | } |
222 | } |
223 | |
224 | |
225 | // ---- writers for combined ANSI strings ---- |
226 | |
227 | impl<'a> fmt::Display for ANSIStrings<'a> { |
228 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
229 | let f: &mut fmt::Write = f; |
230 | self.write_to_any(f) |
231 | } |
232 | } |
233 | |
234 | impl<'a> ANSIByteStrings<'a> { |
235 | /// Write `ANSIByteStrings` to an `io::Write`. This writes the minimal |
236 | /// escape sequences for the associated `Style`s around each set of |
237 | /// bytes. |
238 | pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> { |
239 | let w: &mut io::Write = w; |
240 | self.write_to_any(w) |
241 | } |
242 | } |
243 | |
244 | impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> ANSIGenericStrings<'a, S> |
245 | where <S as ToOwned>::Owned: fmt::Debug, &'a S: AsRef<[u8]> { |
246 | fn write_to_any<W: AnyWrite<wstr=S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> { |
247 | use self::Difference::*; |
248 | |
249 | let first = match self.0.first() { |
250 | None => return Ok(()), |
251 | Some(f) => f, |
252 | }; |
253 | |
254 | write!(w, " {}" , first.style.prefix())?; |
255 | w.write_str(first.string.as_ref())?; |
256 | |
257 | for window in self.0.windows(2) { |
258 | match Difference::between(&window[0].style, &window[1].style) { |
259 | ExtraStyles(style) => write!(w, " {}" , style.prefix())?, |
260 | Reset => write!(w, " {}{}" , RESET, window[1].style.prefix())?, |
261 | NoDifference => {/* Do nothing! */}, |
262 | } |
263 | |
264 | w.write_str(&window[1].string)?; |
265 | } |
266 | |
267 | // Write the final reset string after all of the ANSIStrings have been |
268 | // written, *except* if the last one has no styles, because it would |
269 | // have already been written by this point. |
270 | if let Some(last) = self.0.last() { |
271 | if !last.style.is_plain() { |
272 | write!(w, " {}" , RESET)?; |
273 | } |
274 | } |
275 | |
276 | Ok(()) |
277 | } |
278 | } |
279 | |
280 | |
281 | // ---- tests ---- |
282 | |
283 | #[cfg (test)] |
284 | mod tests { |
285 | pub use super::super::ANSIStrings; |
286 | pub use style::Style; |
287 | pub use style::Colour::*; |
288 | |
289 | #[test ] |
290 | fn no_control_codes_for_plain() { |
291 | let one = Style::default().paint("one" ); |
292 | let two = Style::default().paint("two" ); |
293 | let output = format!(" {}" , ANSIStrings( &[ one, two ] )); |
294 | assert_eq!(&*output, "onetwo" ); |
295 | } |
296 | } |
297 | |