From 1fb5a7903bb44d44e298959d2637d858b2ea8e38 Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 23 Jun 2024 17:07:22 -0700 Subject: document new AnsiDisplaySink, add more -arch tests for sinks --- src/color_new.rs | 63 +++++++++++++++++++++++++++++++++++++-- src/display/display_sink.rs | 4 +++ src/testkit.rs | 4 ++- src/testkit/display.rs | 12 ++++++++ tests/display.rs | 72 ++++++++++++++++++++++++++++++--------------- 5 files changed, 129 insertions(+), 26 deletions(-) diff --git a/src/color_new.rs b/src/color_new.rs index 482a624..ae941f2 100644 --- a/src/color_new.rs +++ b/src/color_new.rs @@ -42,7 +42,57 @@ pub trait YaxColors { fn function(&self) -> Color; } -mod ansi { +/// support for colorizing text with ANSI control sequences. +/// +/// the most useful item in this module is [`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 [`DisplaySink`], including [`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(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` @@ -97,17 +147,26 @@ mod ansi { /// 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! - struct AnsiDisplaySink<'sink, T: DisplaySink, Y: YaxColors> { + pub struct AnsiDisplaySink<'sink, T: DisplaySink, Y: YaxColors> { out: &'sink mut T, span_stack: alloc::vec::Vec, 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() { diff --git a/src/display/display_sink.rs b/src/display/display_sink.rs index 18713cd..9bad1e3 100644 --- a/src/display/display_sink.rs +++ b/src/display/display_sink.rs @@ -350,6 +350,10 @@ impl<'a, T: fmt::Write> FmtSink<'a, T> { pub fn new(f: &'a mut T) -> Self { Self { out: f } } + + pub fn inner_ref(&self) -> &T { + &self.out + } } /// blanket impl that discards all span information, forwards writes to the underlying `fmt::Write` diff --git a/src/testkit.rs b/src/testkit.rs index 6600d80..996b1e4 100644 --- a/src/testkit.rs +++ b/src/testkit.rs @@ -5,4 +5,6 @@ //! [`yaxpeax_arch::display::DisplaySink`], but may grow in the future. #[cfg(feature="alloc")] -pub mod display; +mod display; +#[cfg(feature="alloc")] +pub use display::{DisplaySinkValidator, DisplaySinkWriteComparator}; diff --git a/src/testkit/display.rs b/src/testkit/display.rs index 3cffd49..3cef59c 100644 --- a/src/testkit/display.rs +++ b/src/testkit/display.rs @@ -147,6 +147,18 @@ pub struct DisplaySinkWriteComparator<'sinks, T: DisplaySink, U: DisplaySink> { } impl<'sinks, T: DisplaySink, U: DisplaySink> DisplaySinkWriteComparator<'sinks, T, U> { + pub fn new( + t: &'sinks mut T, t_check: fn(&T) -> &str, + u: &'sinks mut U, u_check: fn(&U) -> &str + ) -> Self { + Self { + sink1: t, + sink1_check: t_check, + sink2: u, + sink2_check: u_check, + } + } + fn compare_sinks(&self) { let sink1_text = (self.sink1_check)(self.sink1); let sink2_text = (self.sink2_check)(self.sink2); diff --git a/tests/display.rs b/tests/display.rs index 887db53..a6d6eb1 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -1,27 +1,5 @@ -/* -#[test] -fn sinks_are_equivalent() { - use yaxpeax_arch::display::NoColorsSink; - use yaxpeax_arch::testkit::DisplaySinkWriteComparator; - - let mut bare = String::new(); - let mut through_sink = String::new(); - for i in 0..u64::MAX { - bare.clear(); - through_sink.clear(); - let mut out = NoColorsSink { - out: &mut through_sink - }; - let mut comparator = DisplaySinkWriteComparator { - sink1: &mut out, - sink1_check: |sink| { sink.out.as_str() }, - sink2: &mut bare, - sink2_check: |sink| { sink.as_str() }, - }; - } -} -*/ +// this was something of a misfeature for these formatters.. #[test] #[allow(deprecated)] fn formatters_are_not_feature_gated() { @@ -86,3 +64,51 @@ fn display_sink_write_hex_helpers() { assert_eq!(buf, expected); } } + +#[cfg(feature="alloc")] +#[test] +fn sinks_are_equivalent() { + use yaxpeax_arch::display::{DisplaySink, FmtSink}; + use yaxpeax_arch::testkit::DisplaySinkWriteComparator; + + let mut bare = String::new(); + let mut through_sink = String::new(); + for i in 0..u16::MAX { + bare.clear(); + through_sink.clear(); + let mut out = FmtSink::new(&mut through_sink); + let mut comparator = DisplaySinkWriteComparator::new( + &mut out, + |sink| { sink.inner_ref().as_str() }, + &mut bare, + |sink| { sink.as_str() }, + ); + comparator.write_u16(i).expect("write succeeds"); + comparator.write_prefixed_u16(i).expect("write succeeds"); + comparator.write_prefixed_i16(i as i16).expect("write succeeds"); + } +} + +#[cfg(all(feature="alloc", feature="color-new"))] +#[test] +fn ansi_sink_works() { + use yaxpeax_arch::color_new::ansi::AnsiDisplaySink; + use yaxpeax_arch::display::DisplaySink; + + let mut buf = String::new(); + + let mut ansi_sink = AnsiDisplaySink::new(&mut buf, yaxpeax_arch::color_new::DefaultColors); + + ansi_sink.span_start_immediate(); + ansi_sink.write_prefixed_u8(0x80).expect("write succeeds"); + ansi_sink.span_end_immediate(); + ansi_sink.write_fixed_size("(").expect("write succeeds"); + ansi_sink.span_start_register(); + ansi_sink.write_fixed_size("rbp").expect("write succeeds"); + ansi_sink.span_end_register(); + ansi_sink.write_fixed_size(")").expect("write succeeds"); + + drop(ansi_sink); + + assert_eq!(buf, "\x1b[37m0x80\x1b[39m(\x1b[38;5;6mrbp\x1b[39m)"); +} -- cgit v1.1