[run_test_suite] Allow the ansi filter to be chained
Updates the ansi filter so that it takes a WriteLine implementator
rather than a Write implementor. This allows chaining together
different WriteLine implementors.
Change-Id: I8670f6bb29ed49b01a4e121d6be94df69a6aed92
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/513991
Reviewed-by: Ankur Mittal <anmittal@google.com>
Reviewed-by: Christopher Johnson <crjohns@google.com>
Commit-Queue: Satsuki Ueno <satsukiu@google.com>
diff --git a/src/sys/run_test_suite/src/writer.rs b/src/sys/run_test_suite/src/writer.rs
index eab01eb..dd3da83 100644
--- a/src/sys/run_test_suite/src/writer.rs
+++ b/src/sys/run_test_suite/src/writer.rs
@@ -2,43 +2,62 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-use std::io::{Error, Write};
+use std::io::{Error, ErrorKind, Write};
use vte::{Parser, Perform};
/// A trait for objects that write line delimited strings.
pub trait WriteLine {
- /// Write a line of output.
- fn write_line(&mut self, s: &str) -> Result<(), Error>;
+ /// Writes a string and terminates it will a line break.
+ fn write_line(&mut self, s: &str) -> Result<(), Error> {
+ self.write_line_segments(&[s])
+ }
+
+ /// Write a series of segments, and terminate it with a line break.
+ fn write_line_segments(&mut self, segments: &[&str]) -> Result<(), Error>;
}
impl<W: Write> WriteLine for W {
- fn write_line(&mut self, s: &str) -> Result<(), Error> {
- writeln!(self, "{}", s)
+ fn write_line_segments(&mut self, segments: &[&str]) -> Result<(), Error> {
+ for segment in segments {
+ self.write_all(segment.as_bytes())?;
+ }
+ writeln!(self)
}
}
impl WriteLine for Box<dyn WriteLine + Send> {
- fn write_line(&mut self, s: &str) -> Result<(), Error> {
- self.as_mut().write_line(s)
+ fn write_line_segments(&mut self, segments: &[&str]) -> Result<(), Error> {
+ self.as_mut().write_line_segments(segments)
}
}
/// A wrapper around a `Write` that filters out ANSI escape sequences before writing to the
/// wrapped object.
-pub struct AnsiFilterWriter<W: Write> {
+pub struct AnsiFilterWriter<W: WriteLine> {
inner: W,
}
-impl<W: Write> AnsiFilterWriter<W> {
+impl<W: WriteLine> AnsiFilterWriter<W> {
pub fn new(inner: W) -> Self {
Self { inner }
}
}
-impl<W: Write> WriteLine for AnsiFilterWriter<W> {
+impl<W: WriteLine> WriteLine for AnsiFilterWriter<W> {
+ fn write_line_segments(&mut self, segments: &[&str]) -> Result<(), Error> {
+ match segments.len() {
+ 1 => self.write_line(segments[0]),
+ // It's possible to do this without this copy. This is currently unused as the ansi
+ // filter is always top of the chain, but if this needs to be nested lower in a chain
+ // of filters we should implement it properly.
+ _ => self.write_line(&segments.join("")),
+ }
+ }
+
fn write_line(&mut self, s: &str) -> Result<(), Error> {
let bytes = s.as_bytes();
let mut parser = Parser::new();
+ let mut segments = vec![];
// Contains range [x1, x2) for the last known chunk of non-ANSI characters
let mut last_known_printable_chunk: Option<(usize, usize)> = None;
@@ -58,7 +77,9 @@
}
// new char is part of a new chunk
(Some(prev_chunk), Some(new_char_idx)) => {
- self.inner.write_all(&bytes[prev_chunk.0..prev_chunk.1])?;
+ let new_segment = std::str::from_utf8(&bytes[prev_chunk.0..prev_chunk.1])
+ .map_err(|_| ErrorKind::InvalidData)?;
+ segments.push(new_segment);
last_known_printable_chunk = Some((new_char_idx, idx + 1));
}
(Some(_), None) => (),
@@ -69,9 +90,11 @@
}
}
if let Some(chunk) = last_known_printable_chunk {
- self.inner.write_all(&bytes[chunk.0..chunk.1])?;
+ let new_segment = std::str::from_utf8(&bytes[chunk.0..chunk.1])
+ .map_err(|_| ErrorKind::InvalidData)?;
+ segments.push(new_segment);
}
- writeln!(self.inner)
+ self.inner.write_line_segments(&segments)
}
}