text_style/
ansi_term.rs

1// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
2// SPDX-License-Identifier: Apache-2.0 or MIT
3
4//! Conversion methods for [`ansi_term`][]’s text style types.
5//!
6//! *Requires the `ansi_term` feature.*
7//!
8//! This module implements these conversions:
9//! - [`Color`][] to [`ansi_term::Color`][]
10//! - [`Style`][] to [`ansi_term::Style`][]
11//! - [`StyledStr`][] and [`StyledString`][] to [`ansi_term::ANSIString`][]
12//!
13//! It also provides the [`render`][] and [`render_iter`][] methods to render strings and iterators
14//! over strings.
15//!
16//! # Examples
17//!
18//! Rendering a single string:
19//!
20//! ```
21//! let s = text_style::StyledStr::plain("test").bold();
22//! text_style::ansi_term::render(std::io::stdout(), s)
23//!     .expect("Failed to render string");
24//! ```
25//!
26//! Rendering multiple strings:
27//!
28//! ```
29//! let v = vec![
30//!     text_style::StyledStr::plain("test").bold(),
31//!     text_style::StyledStr::plain(" "),
32//!     text_style::StyledStr::plain("test2").italic(),
33//! ];
34//! text_style::ansi_term::render_iter(std::io::stdout(), v.iter())
35//!     .expect("Failed to render string");
36//! ```
37//!
38//! [`ansi_term`]: https://docs.rs/ansi_term
39//! [`ansi_term::ANSIString`]: https://docs.rs/ansi_term/latest/ansi_term/type.ANSIString.html
40//! [`ansi_term::Color`]: https://docs.rs/ansi_term/latest/ansi_term/enum.Color.html
41//! [`ansi_term::Style`]: https://docs.rs/ansi_term/latest/ansi_term/struct.Style.html
42//! [`Color`]: ../enum.Color.html
43//! [`Style`]: ../struct.Style.html
44//! [`StyledStr`]: ../struct.StyledStr.html
45//! [`StyledString`]: ../struct.StyledString.html
46//! [`render`]: fn.render.html
47//! [`render_iter`]: fn.render_iter.html
48
49use std::io;
50
51use crate::{AnsiColor, AnsiMode, Color, Style, StyledStr, StyledString};
52
53impl From<Color> for ansi_term::Color {
54    fn from(color: Color) -> ansi_term::Color {
55        match color {
56            Color::Ansi { color, mode } => match mode {
57                AnsiMode::Dark => get_dark_color(color),
58                AnsiMode::Light => get_light_color(color),
59            },
60            Color::Rgb { r, g, b } => ansi_term::Color::RGB(r, g, b),
61        }
62    }
63}
64
65fn get_dark_color(color: AnsiColor) -> ansi_term::Color {
66    match color {
67        AnsiColor::Black => ansi_term::Color::Black,
68        AnsiColor::Red => ansi_term::Color::Red,
69        AnsiColor::Green => ansi_term::Color::Green,
70        AnsiColor::Yellow => ansi_term::Color::Yellow,
71        AnsiColor::Blue => ansi_term::Color::Blue,
72        AnsiColor::Magenta => ansi_term::Color::Purple,
73        AnsiColor::Cyan => ansi_term::Color::Cyan,
74        AnsiColor::White => ansi_term::Color::White,
75    }
76}
77
78fn get_light_color(color: AnsiColor) -> ansi_term::Color {
79    match color {
80        AnsiColor::Black => ansi_term::Color::Fixed(8),
81        AnsiColor::Red => ansi_term::Color::Fixed(9),
82        AnsiColor::Green => ansi_term::Color::Fixed(10),
83        AnsiColor::Yellow => ansi_term::Color::Fixed(11),
84        AnsiColor::Blue => ansi_term::Color::Fixed(12),
85        AnsiColor::Magenta => ansi_term::Color::Fixed(13),
86        AnsiColor::Cyan => ansi_term::Color::Fixed(14),
87        AnsiColor::White => ansi_term::Color::Fixed(15),
88    }
89}
90
91impl From<Style> for ansi_term::Style {
92    fn from(style: Style) -> ansi_term::Style {
93        ansi_term::Style {
94            foreground: style.fg.map(Into::into),
95            background: style.bg.map(Into::into),
96            is_bold: style.effects.is_bold,
97            is_italic: style.effects.is_italic,
98            is_underline: style.effects.is_underline,
99            is_strikethrough: style.effects.is_strikethrough,
100            ..Default::default()
101        }
102    }
103}
104
105impl<'a, 'b> From<&'b StyledStr<'a>> for ansi_term::ANSIString<'a> {
106    fn from(s: &'b StyledStr<'a>) -> ansi_term::ANSIString<'a> {
107        s.style
108            .map_or_else(ansi_term::Style::new, From::from)
109            .paint(s.s)
110    }
111}
112
113impl<'a> From<StyledStr<'a>> for ansi_term::ANSIString<'a> {
114    fn from(s: StyledStr<'a>) -> ansi_term::ANSIString<'a> {
115        s.style
116            .map_or_else(ansi_term::Style::new, From::from)
117            .paint(s.s)
118    }
119}
120
121impl<'a> From<StyledString> for ansi_term::ANSIString<'a> {
122    fn from(s: StyledString) -> ansi_term::ANSIString<'a> {
123        s.style
124            .map_or_else(ansi_term::Style::new, From::from)
125            .paint(s.s)
126    }
127}
128
129/// Renders a styled string to the given output using `ansi_term`.
130///
131/// # Example
132///
133/// ```
134/// let s = text_style::StyledStr::plain("test").bold();
135/// text_style::ansi_term::render(std::io::stdout(), s)
136///     .expect("Failed to render string");
137/// ```
138pub fn render<'a>(mut w: impl io::Write, s: impl Into<StyledStr<'a>>) -> io::Result<()> {
139    write!(w, "{}", ansi_term::ANSIString::from(s.into()))
140}
141
142/// Renders multiple styled string to the given output using `ansi_term`.
143///
144/// This function uses [`ansi_term::ANSIStrings`][] to minimize the written control sequences.
145///
146/// # Example
147///
148/// ```
149/// let v = vec![
150///     text_style::StyledStr::plain("test").bold(),
151///     text_style::StyledStr::plain(" "),
152///     text_style::StyledStr::plain("test2").italic(),
153/// ];
154/// text_style::ansi_term::render_iter(std::io::stdout(), v.iter())
155///     .expect("Failed to render string");
156/// ```
157///
158/// [`ansi_term::ANSIStrings`]: https://docs.rs/ansi_term/latest/ansi_term/fn.ANSIStrings.html
159pub fn render_iter<'a, I, Iter, S, W>(mut w: W, iter: I) -> io::Result<()>
160where
161    I: IntoIterator<Item = S, IntoIter = Iter>,
162    Iter: Iterator<Item = S>,
163    S: Into<StyledStr<'a>>,
164    W: io::Write,
165{
166    let strings: Vec<_> = iter
167        .into_iter()
168        .map(Into::into)
169        .map(ansi_term::ANSIString::from)
170        .collect();
171    write!(w, "{}", ansi_term::ANSIStrings(&strings))
172}