1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
use crate::{rgb::Rgb, Color};

/// Linear color gradient between two color stops
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Gradient {
    /// Start Color of Gradient
    pub start: Rgb,

    /// End Color of Gradient
    pub end: Rgb,
}

impl Gradient {
    /// Creates a new [Gradient] with two [Rgb] colors, `start` and `end`
    #[inline]
    pub const fn new(start: Rgb, end: Rgb) -> Self {
        Self { start, end }
    }
    pub const fn from_color_rgb(start: Color, end: Color) -> Self {
        let start_grad = match start {
            Color::Rgb(r, g, b) => Rgb { r, g, b },
            _ => Rgb { r: 0, g: 0, b: 0 },
        };
        let end_grad = match end {
            Color::Rgb(r, g, b) => Rgb { r, g, b },
            _ => Rgb { r: 0, g: 0, b: 0 },
        };

        Self {
            start: start_grad,
            end: end_grad,
        }
    }

    /// Computes the [Rgb] color between `start` and `end` for `t`
    pub fn at(&self, t: f32) -> Rgb {
        self.start.lerp(self.end, t)
    }

    /// Returns the reverse of `self`
    #[inline]
    pub const fn reverse(&self) -> Self {
        Self::new(self.end, self.start)
    }

    #[allow(dead_code)]
    pub fn build(&self, text: &str, target: TargetGround) -> String {
        let delta = 1.0 / text.len() as f32;
        let mut result = text.char_indices().fold(String::new(), |mut acc, (i, c)| {
            let temp = format!(
                "\x1B[{}m{}",
                self.at(i as f32 * delta).ansi_color_code(target),
                c
            );
            acc.push_str(&temp);
            acc
        });

        result.push_str("\x1B[0m");
        result
    }
}

#[allow(dead_code)]
pub fn build_all_gradient_text(text: &str, foreground: Gradient, background: Gradient) -> String {
    let delta = 1.0 / text.len() as f32;
    let mut result = text.char_indices().fold(String::new(), |mut acc, (i, c)| {
        let step = i as f32 * delta;
        let temp = format!(
            "\x1B[{};{}m{}",
            foreground
                .at(step)
                .ansi_color_code(TargetGround::Foreground),
            background
                .at(step)
                .ansi_color_code(TargetGround::Background),
            c
        );
        acc.push_str(&temp);
        acc
    });

    result.push_str("\x1B[0m");
    result
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetGround {
    Foreground,
    Background,
}

impl TargetGround {
    #[inline]
    pub const fn code(&self) -> u8 {
        match self {
            Self::Foreground => 30,
            Self::Background => 40,
        }
    }
}

pub trait ANSIColorCode {
    fn ansi_color_code(&self, target: TargetGround) -> String;
}