diagnostic/
draw.rs

1use super::*;
2
3/// Output stream to check for whether color is enabled.
4#[derive(Clone, Copy, Debug)]
5pub enum StreamType {
6    /// Standard Output
7    Stdout,
8    /// Standard Error
9    Stderr,
10}
11
12/// A trait used to add formatting attributes to displayable items intended to be written to a
13/// particular stream (`stdout` or `stderr`).
14///
15/// Attributes specified through this trait are not composable (i.e: the behaviour of two nested attributes each with a
16/// conflicting attribute is left unspecified).
17pub trait StreamAwareFmt: Sized {
18    #[cfg(not(feature = "concolor"))]
19    #[doc(hidden)]
20    fn color_enabled_for(_: StreamType) -> bool {
21        true
22    }
23
24    /// Give this value the specified foreground colour, when color is enabled for the specified stream.
25    fn fg<C: Into<Option<Color>>>(self, color: C, stream: StreamType) -> Foreground<Self> {
26        if Self::color_enabled_for(stream) { Foreground(self, color.into()) } else { Foreground(self, None) }
27    }
28
29    /// Give this value the specified background colour, when color is enabled for the specified stream.
30    fn bg<C: Into<Option<Color>>>(self, color: C, stream: StreamType) -> Background<Self> {
31        if Self::color_enabled_for(stream) { Background(self, color.into()) } else { Background(self, None) }
32    }
33}
34
35impl<T: Display> StreamAwareFmt for T {}
36
37/// A trait used to add formatting attributes to displayable items.
38///
39/// If using the `concolor` feature, this trait assumes that the items are going to be printed to
40/// `stderr`. If you are printing to `stdout`, `use` the [`StdoutFmt`] trait instead.
41///
42/// Attributes specified through this trait are not composable (i.e: the behaviour of two nested attributes each with a
43/// conflicting attribute is left unspecified).
44pub trait Console: Sized {
45    /// Give this value the specified foreground colour.
46    fn fg<C: Into<Option<Color>>>(self, color: C) -> Foreground<Self>
47    where
48        Self: Display,
49    {
50        if cfg!(feature = "concolor") {
51            StreamAwareFmt::fg(self, color, StreamType::Stderr)
52        }
53        else {
54            Foreground(self, color.into())
55        }
56    }
57
58    /// Give this value the specified background colour.
59    fn bg<C: Into<Option<Color>>>(self, color: C) -> Background<Self>
60    where
61        Self: Display,
62    {
63        if cfg!(feature = "concolor") {
64            StreamAwareFmt::bg(self, color, StreamType::Stdout)
65        }
66        else {
67            Background(self, color.into())
68        }
69    }
70}
71
72impl<T: Display> Console for T {}
73
74#[derive(Copy, Clone, Debug)]
75pub struct Foreground<T>(T, Option<Color>);
76impl<T: Display> Display for Foreground<T> {
77    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
78        if let Some(col) = self.1 { write!(f, "{}", Paint::new(&self.0).fg(col)) } else { write!(f, "{}", self.0) }
79    }
80}
81
82#[derive(Copy, Clone, Debug)]
83pub struct Background<T>(T, Option<Color>);
84impl<T: Display> Display for Background<T> {
85    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
86        if let Some(col) = self.1 { write!(f, "{}", Paint::new(&self.0).bg(col)) } else { write!(f, "{}", self.0) }
87    }
88}
89
90/// A type that can generate distinct 8-bit colors.
91pub struct Palette {
92    state: [u16; 3],
93    min_brightness: f32,
94}
95
96impl Default for Palette {
97    fn default() -> Self {
98        Self::from_state([30000, 15000, 35000], 0.5)
99    }
100}
101
102impl Palette {
103    /// Create a new [`Palette`] with the given pre-chosen state.
104    ///
105    /// The minimum brightness can be used to control the colour brightness (0.0 - 1.0). The default is 0.5.
106    pub fn from_state(state: [u16; 3], min_brightness: f32) -> Self {
107        Self { state, min_brightness: min_brightness.max(0.0).min(1.0) }
108    }
109
110    /// Create a new [`Palette`] with the default state.
111    pub fn new() -> Self {
112        Self::default()
113    }
114
115    /// Generate the next colour in the sequence.
116    pub fn random(&mut self) -> Color {
117        for i in 0..3 {
118            // magic constant, one of only two that have this property!
119            self.state[i] = (self.state[i] as usize).wrapping_add(40503 * (i * 4 + 1130)) as u16;
120        }
121        Color::Fixed(
122            16 + ((self.state[2] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 5.0
123                + (self.state[1] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 30.0
124                + (self.state[0] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 180.0)
125                as u8,
126        )
127    }
128}