syntax_error/
draw.rs

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/// Output stream to check for whether color is enabled.
82#[derive(Clone, Copy, Debug)]
83pub enum StreamType {
84    /// Standard Output
85    Stdout,
86    /// Standard Error
87    Stderr,
88}
89
90/// A trait used to add formatting attributes to displayable items intended to be written to a
91/// particular stream (`stdout` or `stderr`).
92///
93/// Attributes specified through this trait are not composable (i.e: the behaviour of two nested attributes each with a
94/// conflicting attribute is left unspecified).
95pub trait StreamAwareFmt: Sized {
96    #[cfg(not(feature = "concolor"))]
97    #[doc(hidden)]
98    fn color_enabled_for(_: StreamType) -> bool {
99        true
100    }
101
102    /// Give this value the specified foreground colour, when color is enabled for the specified stream.
103    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    /// Give this value the specified background colour, when color is enabled for the specified stream.
108    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
115/// A trait used to add formatting attributes to displayable items.
116///
117/// If using the `concolor` feature, this trait assumes that the items are going to be printed to
118/// `stderr`. If you are printing to `stdout`, `use` the [`StdoutFmt`] trait instead.
119///
120/// Attributes specified through this trait are not composable (i.e: the behaviour of two nested attributes each with a
121/// conflicting attribute is left unspecified).
122pub trait Fmt: Sized {
123    /// Give this value the specified foreground colour.
124    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    /// Give this value the specified background colour.
137    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
168/// A type that can generate distinct 8-bit colors.
169pub 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    /// Create a new [`ColorGenerator`] with the given pre-chosen state.
182    ///
183    /// The minimum brightness can be used to control the colour brightness (0.0 - 1.0). The default is 0.5.
184    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    /// Create a new [`ColorGenerator`] with the default state.
189    pub fn new() -> Self {
190        Self::default()
191    }
192
193    /// Generate the next colour in the sequence.
194    pub fn next(&mut self) -> Color {
195        for i in 0..3 {
196            // magic constant, one of only two that have this property!
197            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}