aboutsummaryrefslogtreecommitdiff
path: root/src/color_new.rs
blob: 1d3e358f20d5a1378f7bad8599c1189ffeb7f8bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Color {
    Black,
    DarkGrey,
    Red,
    DarkRed,
    Green,
    DarkGreen,
    Yellow,
    DarkYellow,
    Blue,
    DarkBlue,
    Magenta,
    DarkMagenta,
    Cyan,
    DarkCyan,
    White,
    Grey,
}

pub trait YaxColors {
    fn arithmetic_op(&self) -> Color;
    fn stack_op(&self) -> Color;
    fn nop_op(&self) -> Color;
    fn stop_op(&self) -> Color;
    fn control_flow_op(&self) -> Color;
    fn data_op(&self) -> Color;
    fn comparison_op(&self) -> Color;
    fn invalid_op(&self) -> Color;
    fn platform_op(&self) -> Color;
    fn misc_op(&self) -> Color;

    fn register(&self) -> Color;
    fn program_counter(&self) -> Color;
    fn number(&self) -> Color;
    fn zero(&self) -> Color;
    fn one(&self) -> Color;
    fn minus_one(&self) -> Color;
    fn address(&self) -> Color;
    fn symbol(&self) -> Color;
    fn function(&self) -> Color;
}

/// support for colorizing text with ANSI control sequences.
///
/// the most useful item in this module is [`ansi::AnsiDisplaySink`], which interprets span entry
/// and exit as points at which ANSI sequences may need to be written into the output it wraps -
/// that output may be any type implementing [`crate::display::DisplaySink`], including
/// [`crate::display::FmtSink`] to adapt any implementer of `fmt::Write` such as standard out.
///
/// ## example
///
/// to write colored text to standard out:
///
/// ```
/// # #[cfg(feature="alloc")]
/// # {
/// # extern crate alloc;
/// # use alloc::string::String;
/// use yaxpeax_arch::color_new::DefaultColors;
/// use yaxpeax_arch::color_new::ansi::AnsiDisplaySink;
/// use yaxpeax_arch::display::FmtSink;
///
/// let mut s = String::new();
/// let mut s_sink = FmtSink::new(&mut s);
///
/// let mut writer = AnsiDisplaySink::new(&mut s_sink, DefaultColors);
///
/// // this might be a yaxpeax crate's `display_into`, or other library implementation code
/// mod fake_yaxpeax_crate {
///     use yaxpeax_arch::display::DisplaySink;
///
///     pub fn format_memory_operand<T: DisplaySink>(out: &mut T) -> core::fmt::Result {
///         out.span_start_immediate();
///         out.write_prefixed_u8(0x80)?;
///         out.span_end_immediate();
///         out.write_fixed_size("(")?;
///         out.span_start_register();
///         out.write_fixed_size("rbp")?;
///         out.span_end_register();
///         out.write_fixed_size(")")?;
///         Ok(())
///     }
/// }
///
/// // this might be how a user uses `AnsiDisplaySink`, which will write ANSI-ful text to `s` and
/// // print it.
///
/// fake_yaxpeax_crate::format_memory_operand(&mut writer).expect("write succeeds");
///
/// println!("{}", s);
/// # }
/// ```
pub mod ansi {
    use crate::color_new::Color;

    // color sequences as described by ECMA-48 and, apparently, `man 4 console_codes`
    /// translate [`yaxpeax_arch::color_new::Color`] to an ANSI control code that changes the
    /// foreground color to match.
    #[allow(dead_code)] // allowing this to be dead code because if colors are enabled and alloc is not, there will not be an AnsiDisplaySink, which is the sole user of this function.
    fn color2ansi(color: Color) -> &'static str {
        // for most of these, in 256 color space the darker color can be picked by the same color
        // index as the brighter form (from the 8 color command set). dark grey is an outlier,
        // where 38;5;0 and 30 both are black. there is no "grey" in the shorter command set to
        // map to. but it turns out that 38;5;m is exactly the darker grey to use.
        match color {
            Color::Black => "\x1b[30m",
            Color::DarkGrey => "\x1b[38;5;8m",
            Color::Red => "\x1b[31m",
            Color::DarkRed => "\x1b[38;5;1m",
            Color::Green => "\x1b[32m",
            Color::DarkGreen => "\x1b[38;5;2m",
            Color::Yellow => "\x1b[33m",
            Color::DarkYellow => "\x1b[38;5;3m",
            Color::Blue => "\x1b[34m",
            Color::DarkBlue => "\x1b[38;5;4m",
            Color::Magenta => "\x1b[35m",
            Color::DarkMagenta => "\x1b[38;5;5m",
            Color::Cyan => "\x1b[36m",
            Color::DarkCyan => "\x1b[38;5;6m",
            Color::White => "\x1b[37m",
            Color::Grey => "\x1b[38;5;7m",
        }
    }

    // could reasonably be always present, but only used if feature="alloc"
    #[cfg(feature="alloc")]
    const DEFAULT_FG: &'static str = "\x1b[39m";

    #[cfg(feature="alloc")]
    mod ansi_display_sink {
        use crate::color_new::{Color, YaxColors};
        use crate::display::DisplaySink;

        /// adapter to insert ANSI color command sequences in formatted text to style printed
        /// instructions.
        ///
        /// this enables similar behavior as the deprecated [`crate::Colorize`] trait,
        /// for outputs that can process ANSI color commands.
        ///
        /// `AnsiDisplaySink` will silently ignore errors from writes to the underlying `T:
        /// DisplaySink`. when writing to a string or other growable buffer, errors are likely
        /// inseparable from `abort()`. when writing to stdout or stderr, write failures likely
        /// mean output is piped to a process which has closed the pipe but are otherwise harmless.
        /// `span_enter_*` and `span_exit_*` don't have error reporting mechanisms in their return
        /// type, so the only available error mechanism would be to also `abort()`.
        ///
        /// if this turns out to be a bad decision, it'll have to be rethought!
        pub struct AnsiDisplaySink<'sink, T: DisplaySink, Y: YaxColors> {
            out: &'sink mut T,
            span_stack: alloc::vec::Vec<Color>,
            colors: Y
        }

        impl<'sink, T: DisplaySink, Y: YaxColors> AnsiDisplaySink<'sink, T, Y> {
            pub fn new(out: &'sink mut T, colors: Y) -> Self {
                Self {
                    out,
                    span_stack: alloc::vec::Vec::new(),
                    colors,
                }
            }

            fn push_color(&mut self, color: Color) {
                self.span_stack.push(color);
                let _ = self.out.write_fixed_size(super::color2ansi(color));
            }

            fn restore_prev_color(&mut self) {
                let _ = self.span_stack.pop();
                if let Some(prev_color) = self.span_stack.last() {
                    let _ = self.out.write_fixed_size(super::color2ansi(*prev_color));
                } else {
                    let _ = self.out.write_fixed_size(super::DEFAULT_FG);
                };
            }
        }

        impl<'sink, T: DisplaySink, Y: YaxColors> core::fmt::Write for AnsiDisplaySink<'sink, T, Y> {
            fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
                self.out.write_str(s)
            }
            fn write_char(&mut self, c: char) -> Result<(), core::fmt::Error> {
                self.out.write_char(c)
            }
        }

        impl<'sink, T: DisplaySink, Y: YaxColors> DisplaySink for AnsiDisplaySink<'sink, T, Y> {
            fn span_start_immediate(&mut self) { self.push_color(self.colors.number()); }
            fn span_end_immediate(&mut self) { self.restore_prev_color() }

            fn span_start_register(&mut self) { self.push_color(self.colors.register()); }
            fn span_end_register(&mut self) { self.restore_prev_color() }

            // ah.. the right way, currently, to colorize opcodes would be to collect text while in the
            // opcode span, and request some kind of user-provided decoder ring to translate mnemonics
            // into the right color. that's very unfortunate. maybe there should be another span for
            // `opcode_kind(u8)` for impls to report what kind of opcode they'll be emitting..
            fn span_start_opcode(&mut self) { self.push_color(self.colors.misc_op()); }
            fn span_end_opcode(&mut self) { self.restore_prev_color() }

            fn span_start_program_counter(&mut self) { self.push_color(self.colors.program_counter()); }
            fn span_end_program_counter(&mut self) { self.restore_prev_color() }

            fn span_start_number(&mut self) { self.push_color(self.colors.number()); }
            fn span_end_number(&mut self) { self.restore_prev_color() }

            fn span_start_address(&mut self) { self.push_color(self.colors.address()); }
            fn span_end_address(&mut self) { self.restore_prev_color() }

            fn span_start_function_expr(&mut self) { self.push_color(self.colors.function()); }
            fn span_end_function_expr(&mut self) { self.restore_prev_color() }
        }
    }
    #[cfg(feature="alloc")]
    pub use ansi_display_sink::AnsiDisplaySink;
}

pub struct DefaultColors;

impl YaxColors for DefaultColors {
    fn arithmetic_op(&self) -> Color {
        Color::Yellow
    }
    fn stack_op(&self) -> Color {
        Color::DarkMagenta
    }
    fn nop_op(&self) -> Color {
        Color::DarkBlue
    }
    fn stop_op(&self) -> Color {
        Color::Red
    }
    fn control_flow_op(&self) -> Color {
        Color::DarkGreen
    }
    fn data_op(&self) -> Color {
        Color::Magenta
    }
    fn comparison_op(&self) -> Color {
        Color::DarkYellow
    }
    fn invalid_op(&self) -> Color {
        Color::DarkRed
    }
    fn misc_op(&self) -> Color {
        Color::Cyan
    }
    fn platform_op(&self) -> Color {
        Color::DarkCyan
    }

    fn register(&self) -> Color {
        Color::DarkCyan
    }
    fn program_counter(&self) -> Color {
        Color::DarkRed
    }
    fn number(&self) -> Color {
        Color::White
    }
    fn zero(&self) -> Color {
        Color::White
    }
    fn one(&self) -> Color {
        Color::White
    }
    fn minus_one(&self) -> Color {
        Color::White
    }
    fn address(&self) -> Color {
        Color::DarkGreen
    }
    fn symbol(&self) -> Color {
        Color::Green
    }
    fn function(&self) -> Color {
        Color::Green
    }
}