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
90pub trait StreamAwareFmt: Sized {
96 #[cfg(not(feature = "concolor"))]
97 #[doc(hidden)]
98 fn color_enabled_for(_: StreamType) -> bool {
99 true
100 }
101
102 fn fg<C: Into<Option<Color>>>(self, color: C, stream: StreamType) -> Foreground<Self> {
104 if Self::color_enabled_for(stream) { Foreground(self, color.into()) } else { Foreground(self, None) }
105 }
106
107 fn bg<C: Into<Option<Color>>>(self, color: C, stream: StreamType) -> Background<Self> {
109 if Self::color_enabled_for(stream) { Background(self, color.into()) } else { Background(self, None) }
110 }
111}
112
113impl<T: Display> StreamAwareFmt for T {}
114
115pub trait Fmt: Sized {
123 fn fg<C: Into<Option<Color>>>(self, color: C) -> Foreground<Self>
125 where
126 Self: Display,
127 {
128 if cfg!(feature = "concolor") {
129 StreamAwareFmt::fg(self, color, StreamType::Stderr)
130 }
131 else {
132 Foreground(self, color.into())
133 }
134 }
135
136 fn bg<C: Into<Option<Color>>>(self, color: C) -> Background<Self>
138 where
139 Self: Display,
140 {
141 if cfg!(feature = "concolor") {
142 StreamAwareFmt::bg(self, color, StreamType::Stdout)
143 }
144 else {
145 Background(self, color.into())
146 }
147 }
148}
149
150impl<T: Display> Fmt for T {}
151
152#[derive(Copy, Clone, Debug)]
153pub struct Foreground<T>(T, Option<Color>);
154impl<T: Display> Display for Foreground<T> {
155 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
156 if let Some(col) = self.1 { write!(f, "{}", Paint::new(&self.0).fg(col)) } else { write!(f, "{}", self.0) }
157 }
158}
159
160#[derive(Copy, Clone, Debug)]
161pub struct Background<T>(T, Option<Color>);
162impl<T: Display> Display for Background<T> {
163 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
164 if let Some(col) = self.1 { write!(f, "{}", Paint::new(&self.0).bg(col)) } else { write!(f, "{}", self.0) }
165 }
166}
167
168pub struct ColorGenerator {
170 state: [u16; 3],
171 min_brightness: f32,
172}
173
174impl Default for ColorGenerator {
175 fn default() -> Self {
176 Self::from_state([30000, 15000, 35000], 0.5)
177 }
178}
179
180impl ColorGenerator {
181 pub fn from_state(state: [u16; 3], min_brightness: f32) -> Self {
185 Self { state, min_brightness: min_brightness.max(0.0).min(1.0) }
186 }
187
188 pub fn new() -> Self {
190 Self::default()
191 }
192
193 pub fn next(&mut self) -> Color {
195 for i in 0..3 {
196 self.state[i] = (self.state[i] as usize).wrapping_add(40503 * (i * 4 + 1130)) as u16;
198 }
199 Color::Fixed(
200 16 + ((self.state[2] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 5.0
201 + (self.state[1] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 30.0
202 + (self.state[0] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 180.0)
203 as u8,
204 )
205 }
206}