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}