1use super::*;
2use yansi::Paint;
3
4pub struct Characters {
5    pub hbar: char,
6    pub vbar: char,
7    pub xbar: char,
8    pub vbar_break: char,
9    pub vbar_gap: char,
10
11    pub uarrow: char,
12    pub rarrow: char,
13
14    pub ltop: char,
15    pub mtop: char,
16    pub rtop: char,
17    pub lbot: char,
18    pub rbot: char,
19    pub mbot: char,
20
21    pub lbox: char,
22    pub rbox: char,
23
24    pub lcross: char,
25    pub rcross: char,
26
27    pub underbar: char,
28    pub underline: char,
29}
30
31impl Characters {
32    pub fn unicode() -> Self {
33        Self {
34            hbar: '─',
35            vbar: '│',
36            xbar: '┼',
37            vbar_break: '┆',
38            vbar_gap: '┆',
39            uarrow: '🭯',
40            rarrow: '▶',
41            ltop: '╭',
42            mtop: '┬',
43            rtop: '╮',
44            lbot: '╰',
45            mbot: '┴',
46            rbot: '╯',
47            lbox: '[',
48            rbox: ']',
49            lcross: '├',
50            rcross: '┤',
51            underbar: '┬',
52            underline: '─',
53        }
54    }
55
56    pub fn ascii() -> Self {
57        Self {
58            hbar: '-',
59            vbar: '|',
60            xbar: '+',
61            vbar_break: '*',
62            vbar_gap: ':',
63            uarrow: '^',
64            rarrow: '>',
65            ltop: ',',
66            mtop: 'v',
67            rtop: '.',
68            lbot: '`',
69            mbot: '^',
70            rbot: '\'',
71            lbox: '[',
72            rbox: ']',
73            lcross: '|',
74            rcross: '|',
75            underbar: '|',
76            underline: '^',
77        }
78    }
79}
80
81#[derive(Clone, Copy, Debug)]
83pub enum StreamType {
84    Stdout,
86    Stderr,
88}
89
90#[cfg(feature = "concolor")]
91impl From<StreamType> for concolor::Stream {
92    fn from(s: StreamType) -> Self {
93        match s {
94            StreamType::Stdout => concolor::Stream::Stdout,
95            StreamType::Stderr => concolor::Stream::Stderr,
96        }
97    }
98}
99
100pub trait StreamAwareFmt: Sized {
106    #[cfg(feature = "concolor")]
107    fn color_enabled_for(s: StreamType) -> bool {
109        concolor::get(s.into()).color()
110    }
111
112    #[cfg(not(feature = "concolor"))]
113    #[doc(hidden)]
114    fn color_enabled_for(_: StreamType) -> bool {
115        true
116    }
117
118    fn fg<C: Into<Option<Color>>>(self, color: C, stream: StreamType) -> Foreground<Self> {
120        if Self::color_enabled_for(stream) {
121            Foreground(self, color.into())
122        } else {
123            Foreground(self, None)
124        }
125    }
126
127    fn bg<C: Into<Option<Color>>>(self, color: C, stream: StreamType) -> Background<Self> {
129        if Self::color_enabled_for(stream) {
130            Background(self, color.into())
131        } else {
132            Background(self, None)
133        }
134    }
135}
136
137impl<T: fmt::Display> StreamAwareFmt for T {}
138
139pub trait Fmt: Sized {
147    fn fg<C: Into<Option<Color>>>(self, color: C) -> Foreground<Self>
149    where
150        Self: fmt::Display,
151    {
152        if cfg!(feature = "concolor") {
153            StreamAwareFmt::fg(self, color, StreamType::Stderr)
154        } else {
155            Foreground(self, color.into())
156        }
157    }
158
159    fn bg<C: Into<Option<Color>>>(self, color: C) -> Background<Self>
161    where
162        Self: fmt::Display,
163    {
164        if cfg!(feature = "concolor") {
165            StreamAwareFmt::bg(self, color, StreamType::Stdout)
166        } else {
167            Background(self, color.into())
168        }
169    }
170}
171
172impl<T: fmt::Display> Fmt for T {}
173
174#[cfg(any(feature = "concolor", doc))]
179pub trait StdoutFmt: StreamAwareFmt {
180    fn fg<C: Into<Option<Color>>>(self, color: C) -> Foreground<Self> {
182        StreamAwareFmt::fg(self, color, StreamType::Stdout)
183    }
184
185    fn bg<C: Into<Option<Color>>>(self, color: C) -> Background<Self> {
187        StreamAwareFmt::bg(self, color, StreamType::Stdout)
188    }
189}
190
191#[cfg(feature = "concolor")]
192impl<T: fmt::Display> StdoutFmt for T {}
193
194#[derive(Copy, Clone, Debug)]
195pub struct Foreground<T>(T, Option<Color>);
196impl<T: fmt::Display> fmt::Display for Foreground<T> {
197    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198        if let Some(col) = self.1 {
199            write!(f, "{}", Paint::new(&self.0).fg(col))
200        } else {
201            write!(f, "{}", self.0)
202        }
203    }
204}
205
206#[derive(Copy, Clone, Debug)]
207pub struct Background<T>(T, Option<Color>);
208impl<T: fmt::Display> fmt::Display for Background<T> {
209    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210        if let Some(col) = self.1 {
211            write!(f, "{}", Paint::new(&self.0).bg(col))
212        } else {
213            write!(f, "{}", self.0)
214        }
215    }
216}
217
218pub struct ColorGenerator {
220    state: [u16; 3],
221    min_brightness: f32,
222}
223
224impl Default for ColorGenerator {
225    fn default() -> Self { Self::from_state([30000, 15000, 35000], 0.5) }
226}
227
228impl ColorGenerator {
229    pub fn from_state(state: [u16; 3], min_brightness: f32) -> Self {
233        Self { state, min_brightness: min_brightness.max(0.0).min(1.0) }
234    }
235
236    pub fn new() -> Self {
238        Self::default()
239    }
240
241    pub fn next(&mut self) -> Color {
243        for i in 0..3 {
244            self.state[i] = (self.state[i] as usize).wrapping_add(40503 * (i * 4 + 1130)) as u16;
246        }
247        Color::Fixed(16
248            + ((self.state[2] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 5.0
249            + (self.state[1] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 30.0
250            + (self.state[0] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 180.0) as u8)
251    }
252}