/rust/registry/src/index.crates.io-6f17d22bba15001f/nu-ansi-term-0.46.0/src/display.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use crate::ansi::RESET; |
2 | | use crate::difference::Difference; |
3 | | use crate::style::{Color, Style}; |
4 | | use crate::write::AnyWrite; |
5 | | use std::borrow::Cow; |
6 | | use std::fmt; |
7 | | use 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)] |
13 | | pub struct AnsiGenericString<'a, S: 'a + ToOwned + ?Sized> |
14 | | where |
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 | | /// ``` |
32 | | impl<'a, S: 'a + ToOwned + ?Sized> Clone for AnsiGenericString<'a, S> |
33 | | where |
34 | | <S as ToOwned>::Owned: fmt::Debug, |
35 | | { |
36 | 0 | fn clone(&self) -> AnsiGenericString<'a, S> { |
37 | 0 | AnsiGenericString { |
38 | 0 | style: self.style, |
39 | 0 | string: self.string.clone(), |
40 | 0 | } |
41 | 0 | } |
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 | | /// ``` |
86 | | pub 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. |
90 | | pub type AnsiByteString<'a> = AnsiGenericString<'a, [u8]>; |
91 | | |
92 | | impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for AnsiGenericString<'a, S> |
93 | | where |
94 | | I: Into<Cow<'a, S>>, |
95 | | <S as ToOwned>::Owned: fmt::Debug, |
96 | | { |
97 | 0 | fn from(input: I) -> AnsiGenericString<'a, S> { |
98 | 0 | AnsiGenericString { |
99 | 0 | string: input.into(), |
100 | 0 | style: Style::default(), |
101 | 0 | } |
102 | 0 | } |
103 | | } |
104 | | |
105 | | impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S> |
106 | | where |
107 | | <S as ToOwned>::Owned: fmt::Debug, |
108 | | { |
109 | | /// Directly access the style |
110 | 0 | pub fn style_ref(&self) -> &Style { |
111 | 0 | &self.style |
112 | 0 | } |
113 | | |
114 | | /// Directly access the style mutably |
115 | 0 | pub fn style_ref_mut(&mut self) -> &mut Style { |
116 | 0 | &mut self.style |
117 | 0 | } Unexecuted instantiation: <nu_ansi_term::display::AnsiGenericString<str>>::style_ref_mut Unexecuted instantiation: <nu_ansi_term::display::AnsiGenericString<_>>::style_ref_mut |
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)] |
123 | | pub struct AnsiGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [AnsiGenericString<'a, S>]) |
124 | | where |
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. |
130 | | pub type AnsiStrings<'a> = AnsiGenericStrings<'a, str>; |
131 | | |
132 | | /// A function to construct an `AnsiStrings` instance. |
133 | | #[allow(non_snake_case)] |
134 | 0 | pub fn AnsiStrings<'a>(arg: &'a [AnsiString<'a>]) -> AnsiStrings<'a> { |
135 | 0 | AnsiGenericStrings(arg) |
136 | 0 | } |
137 | | |
138 | | /// A set of `AnsiByteString`s collected together, in order to be |
139 | | /// written with a minimum of control characters. |
140 | | pub type AnsiByteStrings<'a> = AnsiGenericStrings<'a, [u8]>; |
141 | | |
142 | | /// A function to construct an `AnsiByteStrings` instance. |
143 | | #[allow(non_snake_case)] |
144 | 0 | pub fn AnsiByteStrings<'a>(arg: &'a [AnsiByteString<'a>]) -> AnsiByteStrings<'a> { |
145 | 0 | AnsiGenericStrings(arg) |
146 | 0 | } |
147 | | |
148 | | // ---- paint functions ---- |
149 | | |
150 | | impl Style { |
151 | | /// Paints the given text with this color, returning an ANSI string. |
152 | | #[must_use] |
153 | 0 | pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S> |
154 | 0 | where |
155 | 0 | I: Into<Cow<'a, S>>, |
156 | 0 | <S as ToOwned>::Owned: fmt::Debug, |
157 | 0 | { |
158 | 0 | AnsiGenericString { |
159 | 0 | string: input.into(), |
160 | 0 | style: self, |
161 | 0 | } |
162 | 0 | } Unexecuted instantiation: <nu_ansi_term::style::Style>::paint::<&str, str> Unexecuted instantiation: <nu_ansi_term::style::Style>::paint::<alloc::string::String, str> |
163 | | } |
164 | | |
165 | | impl 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 | 0 | pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S> |
176 | 0 | where |
177 | 0 | I: Into<Cow<'a, S>>, |
178 | 0 | <S as ToOwned>::Owned: fmt::Debug, |
179 | 0 | { |
180 | 0 | AnsiGenericString { |
181 | 0 | string: input.into(), |
182 | 0 | style: self.normal(), |
183 | 0 | } |
184 | 0 | } Unexecuted instantiation: <nu_ansi_term::style::Color>::paint::<&str, str> Unexecuted instantiation: <nu_ansi_term::style::Color>::paint::<_, _> |
185 | | } |
186 | | |
187 | | // ---- writers for individual ANSI strings ---- |
188 | | |
189 | | impl<'a> fmt::Display for AnsiString<'a> { |
190 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
191 | 0 | let w: &mut dyn fmt::Write = f; |
192 | 0 | self.write_to_any(w) |
193 | 0 | } |
194 | | } |
195 | | |
196 | | impl<'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 | 0 | pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> { |
200 | 0 | let w: &mut dyn io::Write = w; |
201 | 0 | self.write_to_any(w) |
202 | 0 | } |
203 | | } |
204 | | |
205 | | impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S> |
206 | | where |
207 | | <S as ToOwned>::Owned: fmt::Debug, |
208 | | &'a S: AsRef<[u8]>, |
209 | | { |
210 | 0 | fn write_to_any<W: AnyWrite<Wstr = S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> { |
211 | 0 | write!(w, "{}", self.style.prefix())?; |
212 | 0 | w.write_str(self.string.as_ref())?; |
213 | 0 | write!(w, "{}", self.style.suffix()) |
214 | 0 | } |
215 | | } |
216 | | |
217 | | // ---- writers for combined ANSI strings ---- |
218 | | |
219 | | impl<'a> fmt::Display for AnsiStrings<'a> { |
220 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
221 | 0 | let f: &mut dyn fmt::Write = f; |
222 | 0 | self.write_to_any(f) |
223 | 0 | } |
224 | | } |
225 | | |
226 | | impl<'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 | 0 | pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> { |
231 | 0 | let w: &mut dyn io::Write = w; |
232 | 0 | self.write_to_any(w) |
233 | 0 | } |
234 | | } |
235 | | |
236 | | impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> AnsiGenericStrings<'a, S> |
237 | | where |
238 | | <S as ToOwned>::Owned: fmt::Debug, |
239 | | &'a S: AsRef<[u8]>, |
240 | | { |
241 | 0 | fn write_to_any<W: AnyWrite<Wstr = S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> { |
242 | | use self::Difference::*; |
243 | | |
244 | 0 | let first = match self.0.first() { |
245 | 0 | None => return Ok(()), |
246 | 0 | Some(f) => f, |
247 | 0 | }; |
248 | 0 |
|
249 | 0 | write!(w, "{}", first.style.prefix())?; |
250 | 0 | w.write_str(first.string.as_ref())?; |
251 | | |
252 | 0 | for window in self.0.windows(2) { |
253 | 0 | match Difference::between(&window[0].style, &window[1].style) { |
254 | 0 | ExtraStyles(style) => write!(w, "{}", style.prefix())?, |
255 | 0 | Reset => write!(w, "{}{}", RESET, window[1].style.prefix())?, |
256 | 0 | Empty => { /* Do nothing! */ } |
257 | | } |
258 | | |
259 | 0 | 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 | 0 | if let Some(last) = self.0.last() { |
266 | 0 | if !last.style.is_plain() { |
267 | 0 | write!(w, "{}", RESET)?; |
268 | 0 | } |
269 | 0 | } |
270 | | |
271 | 0 | Ok(()) |
272 | 0 | } |
273 | | } |
274 | | |
275 | | // ---- tests ---- |
276 | | |
277 | | #[cfg(test)] |
278 | | mod 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 | | } |