text_style/crossterm.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 [`crossterm`][]’s text style types.
5//!
6//! *Requires the `crossterm` feature.*
7//!
8//! This module implements these conversions:
9//! - [`Color`][] to [`crossterm::style::Color`][]
10//! - [`Effect`][] to [`crossterm::style::Attribute`][]
11//! - [`Effects`][] to [`crossterm::style::Attributes`][]
12//! - [`Style`][] to [`crossterm::style::ContentStyle`][]
13//! - [`StyledStr`][] and [`StyledString`][] to [`crossterm::style::StyledContent`][]
14//!
15//! It also provides the [`render`][] and [`render_iter`][] methods to render strings and iterators
16//! over strings.
17//!
18//! # Examples
19//!
20//! Rendering a single string:
21//!
22//! ```
23//! let s = text_style::StyledStr::plain("test").bold();
24//! text_style::crossterm::render(std::io::stdout(), s)
25//! .expect("Failed to render string");
26//! ```
27//!
28//! Rendering multiple strings:
29//!
30//! ```
31//! let v = vec![
32//! text_style::StyledStr::plain("test").bold(),
33//! text_style::StyledStr::plain(" "),
34//! text_style::StyledStr::plain("test2").italic(),
35//! ];
36//! text_style::crossterm::render_iter(std::io::stdout(), v.iter())
37//! .expect("Failed to render string");
38//! ```
39//!
40//! [`crossterm`]: https://docs.rs/crossterm
41//! [`crossterm::style::Attribute`]: https://docs.rs/crossterm/latest/crossterm/style/enum.Attribute.html
42//! [`crossterm::style::Attributes`]: https://docs.rs/crossterm/latest/crossterm/style/struct.Attributes.html
43//! [`crossterm::style::Color`]: https://docs.rs/crossterm/latest/crossterm/style/enum.Color.html
44//! [`crossterm::style::ContentStyle`]: https://docs.rs/crossterm/latest/crossterm/style/struct.ContentStyle.html
45//! [`crossterm::style::StyledContent`]: https://docs.rs/crossterm/latest/crossterm/style/struct.StyledContent.html
46//! [`Color`]: ../enum.Color.html
47//! [`Effect`]: ../enum.Effect.html
48//! [`Effects`]: ../struct.Effects.html
49//! [`Style`]: ../struct.Style.html
50//! [`StyledStr`]: ../struct.StyledStr.html
51//! [`StyledString`]: ../struct.StyledString.html
52//! [`render`]: fn.render.html
53//! [`render_iter`]: fn.render_iter.html
54
55use std::io;
56
57use crossterm::style;
58
59use crate::{AnsiColor, AnsiMode, Color, Effect, Effects, Style, StyledStr, StyledString};
60
61impl From<Color> for style::Color {
62 fn from(color: Color) -> style::Color {
63 use AnsiColor::*;
64 use AnsiMode::*;
65
66 match color {
67 Color::Ansi { color, mode } => match (mode, color) {
68 (Dark, Black) => style::Color::Black,
69 (Dark, Red) => style::Color::DarkRed,
70 (Dark, Green) => style::Color::DarkGreen,
71 (Dark, Yellow) => style::Color::DarkYellow,
72 (Dark, Blue) => style::Color::DarkBlue,
73 (Dark, Magenta) => style::Color::DarkMagenta,
74 (Dark, Cyan) => style::Color::DarkCyan,
75 // TODO: check greys
76 (Dark, White) => style::Color::Grey,
77 (Light, Black) => style::Color::DarkGrey,
78 (Light, Red) => style::Color::Red,
79 (Light, Green) => style::Color::Green,
80 (Light, Yellow) => style::Color::Yellow,
81 (Light, Blue) => style::Color::Blue,
82 (Light, Magenta) => style::Color::Magenta,
83 (Light, Cyan) => style::Color::Cyan,
84 (Light, White) => style::Color::White,
85 },
86 Color::Rgb { r, g, b } => style::Color::Rgb { r, g, b },
87 }
88 }
89}
90
91impl From<Effect> for style::Attribute {
92 fn from(effect: Effect) -> style::Attribute {
93 match effect {
94 Effect::Bold => style::Attribute::Bold,
95 Effect::Italic => style::Attribute::Italic,
96 Effect::Underline => style::Attribute::Underlined,
97 Effect::Strikethrough => style::Attribute::CrossedOut,
98 }
99 }
100}
101
102impl From<Effects> for style::Attributes {
103 fn from(effects: Effects) -> style::Attributes {
104 let mut attributes = style::Attributes::default();
105 for effect in effects {
106 attributes.set(effect.into());
107 }
108 attributes
109 }
110}
111
112impl From<Style> for style::ContentStyle {
113 fn from(style: Style) -> style::ContentStyle {
114 style::ContentStyle {
115 foreground_color: style.fg.map(Into::into),
116 background_color: style.bg.map(Into::into),
117 attributes: style.effects.into(),
118 }
119 }
120}
121
122impl<'a, 'b> From<&'b StyledStr<'a>> for style::StyledContent<&'a str> {
123 fn from(s: &'b StyledStr<'a>) -> style::StyledContent<&'a str> {
124 style::StyledContent::new(s.style.map(Into::into).unwrap_or_default(), s.s)
125 }
126}
127
128impl<'a> From<StyledStr<'a>> for style::StyledContent<&'a str> {
129 fn from(s: StyledStr<'a>) -> style::StyledContent<&'a str> {
130 style::StyledContent::new(s.style.map(Into::into).unwrap_or_default(), s.s)
131 }
132}
133
134impl From<StyledString> for style::StyledContent<String> {
135 fn from(s: StyledString) -> style::StyledContent<String> {
136 style::StyledContent::new(s.style.map(Into::into).unwrap_or_default(), s.s)
137 }
138}
139
140/// Renders a styled string to the given output using `crossterm`.
141///
142/// # Example
143///
144/// ```
145/// let s = text_style::StyledStr::plain("test").bold();
146/// text_style::crossterm::render(std::io::stdout(), s)
147/// .expect("Failed to render string");
148/// ```
149pub fn render<'a>(mut w: impl io::Write, s: impl Into<StyledStr<'a>>) -> crossterm::Result<()> {
150 use crossterm::ExecutableCommand;
151
152 w.execute(crossterm::style::PrintStyledContent(s.into().into()))
153 .map(|_| {})
154}
155
156/// Renders multiple styled string to the given output using `crossterm`.
157///
158/// This function queues the draw commands, so the output has to be flushed by the caller.
159///
160/// # Example
161///
162/// ```
163/// let v = vec![
164/// text_style::StyledStr::plain("test").bold(),
165/// text_style::StyledStr::plain(" "),
166/// text_style::StyledStr::plain("test2").italic(),
167/// ];
168/// text_style::crossterm::render_iter(std::io::stdout(), v.iter())
169/// .expect("Failed to render string");
170/// ```
171pub fn render_iter<'a, I, Iter, S, W>(mut w: W, iter: I) -> crossterm::Result<()>
172where
173 I: IntoIterator<Item = S, IntoIter = Iter>,
174 Iter: Iterator<Item = S>,
175 S: Into<StyledStr<'a>>,
176 W: io::Write,
177{
178 use crossterm::QueueableCommand;
179
180 for s in iter {
181 w.queue(crossterm::style::PrintStyledContent(s.into().into()))?;
182 }
183 Ok(())
184}