nu_ansi_term/
gradient.rs

1use crate::{rgb::Rgb, Color};
2
3/// Linear color gradient between two color stops
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Gradient {
6    /// Start Color of Gradient
7    pub start: Rgb,
8
9    /// End Color of Gradient
10    pub end: Rgb,
11}
12
13impl Gradient {
14    /// Creates a new [Gradient] with two [Rgb] colors, `start` and `end`
15    #[inline]
16    pub const fn new(start: Rgb, end: Rgb) -> Self {
17        Self { start, end }
18    }
19
20    pub const fn from_color_rgb(start: Color, end: Color) -> Self {
21        let start_grad = match start {
22            Color::Rgb(r, g, b) => Rgb { r, g, b },
23            _ => Rgb { r: 0, g: 0, b: 0 },
24        };
25        let end_grad = match end {
26            Color::Rgb(r, g, b) => Rgb { r, g, b },
27            _ => Rgb { r: 0, g: 0, b: 0 },
28        };
29
30        Self {
31            start: start_grad,
32            end: end_grad,
33        }
34    }
35
36    /// Computes the [Rgb] color between `start` and `end` for `t`
37    pub fn at(&self, t: f32) -> Rgb {
38        self.start.lerp(self.end, t)
39    }
40
41    /// Returns the reverse of `self`
42    #[inline]
43    pub const fn reverse(&self) -> Self {
44        Self::new(self.end, self.start)
45    }
46
47    pub fn build(&self, text: &str, target: TargetGround) -> String {
48        let delta = 1.0 / text.len() as f32;
49        let mut result = text.char_indices().fold(String::new(), |mut acc, (i, c)| {
50            let temp = format!(
51                "\x1B[{}m{}",
52                self.at(i as f32 * delta).ansi_color_code(target),
53                c
54            );
55            acc.push_str(&temp);
56            acc
57        });
58
59        result.push_str("\x1B[0m");
60        result
61    }
62}
63
64pub fn build_all_gradient_text(text: &str, foreground: Gradient, background: Gradient) -> String {
65    let delta = 1.0 / text.len() as f32;
66    let mut result = text.char_indices().fold(String::new(), |mut acc, (i, c)| {
67        let step = i as f32 * delta;
68        let temp = format!(
69            "\x1B[{};{}m{}",
70            foreground
71                .at(step)
72                .ansi_color_code(TargetGround::Foreground),
73            background
74                .at(step)
75                .ansi_color_code(TargetGround::Background),
76            c
77        );
78        acc.push_str(&temp);
79        acc
80    });
81
82    result.push_str("\x1B[0m");
83    result
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum TargetGround {
88    Foreground,
89    Background,
90}
91
92impl TargetGround {
93    #[inline]
94    pub const fn code(&self) -> u8 {
95        match self {
96            Self::Foreground => 30,
97            Self::Background => 40,
98        }
99    }
100}
101
102pub trait ANSIColorCode {
103    fn ansi_color_code(&self, target: TargetGround) -> String;
104}