Coverage Report

Created: 2025-02-25 06:39

/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
}