@@ -98,6 +98,18 @@ pub fn find(needle: &ReadUntil, buffer: &str, eof: bool) -> Option<(usize, usize
9898 }
9999}
100100
101+ /// Options for NBReader
102+ ///
103+ /// - timeout:
104+ /// + `None`: read_until is blocking forever. This is probably not what you want
105+ /// + `Some(millis)`: after millis milliseconds a timeout error is raised
106+ /// - strip_ansi_escape_codes: Whether to filter out escape codes, such as colors.
107+ #[ derive( Default ) ]
108+ pub struct Options {
109+ pub timeout_ms : Option < u64 > ,
110+ pub strip_ansi_escape_codes : bool ,
111+ }
112+
101113/// Non blocking reader
102114///
103115/// Typically you'd need that to check for output of a process without blocking your thread.
@@ -116,16 +128,16 @@ impl NBReader {
116128 /// # Arguments:
117129 ///
118130 /// - f: file like object
119- /// - timeout:
120- /// + `None`: read_until is blocking forever. This is probably not what you want
121- /// + `Some(millis)`: after millis milliseconds a timeout error is raised
122- pub fn new < R : Read + Send + ' static > ( f : R , timeout : Option < u64 > ) -> NBReader {
131+ /// - options: see `Options`
132+ pub fn new < R : Read + Send + ' static > ( f : R , options : Options ) -> NBReader {
123133 let ( tx, rx) = channel ( ) ;
124134
125135 // spawn a thread which reads one char and sends it to tx
126136 thread:: spawn ( move || -> Result < ( ) , Error > {
127137 let mut reader = BufReader :: new ( f) ;
128138 let mut byte = [ 0u8 ] ;
139+ let mut in_escape_code = false ;
140+
129141 loop {
130142 match reader. read ( & mut byte) {
131143 Ok ( 0 ) => {
@@ -134,8 +146,16 @@ impl NBReader {
134146 break ;
135147 }
136148 Ok ( _) => {
137- tx. send ( Ok ( PipedChar :: Char ( byte[ 0 ] ) ) )
138- . map_err ( |_| Error :: MpscSendError ) ?;
149+ if options. strip_ansi_escape_codes && byte[ 0 ] == 27 {
150+ in_escape_code = true ;
151+ } else if options. strip_ansi_escape_codes && in_escape_code {
152+ if char:: from ( byte[ 0 ] ) . is_alphabetic ( ) {
153+ in_escape_code = false ;
154+ }
155+ } else {
156+ tx. send ( Ok ( PipedChar :: Char ( byte[ 0 ] ) ) )
157+ . map_err ( |_| Error :: MpscSendError ) ?;
158+ }
139159 }
140160 Err ( error) => {
141161 tx. send ( Err ( PipeError :: IO ( error) ) )
@@ -153,7 +173,7 @@ impl NBReader {
153173 reader : rx,
154174 buffer : String :: with_capacity ( 1024 ) ,
155175 eof : false ,
156- timeout : timeout . map ( time:: Duration :: from_millis) ,
176+ timeout : options . timeout_ms . map ( time:: Duration :: from_millis) ,
157177 }
158178 }
159179
@@ -204,11 +224,11 @@ impl NBReader {
204224 ///
205225 /// ```
206226 /// # use std::io::Cursor;
207- /// use rexpect::reader::{NBReader, ReadUntil, Regex};
227+ /// use rexpect::reader::{NBReader, ReadUntil, Regex, Options };
208228 /// // instead of a Cursor you would put your process output or file here
209229 /// let f = Cursor::new("Hello, miss!\n\
210230 /// What do you mean: 'miss'?");
211- /// let mut e = NBReader::new(f, None );
231+ /// let mut e = NBReader::new(f, Options::default() );
212232 ///
213233 /// let (first_line, _) = e.read_until(&ReadUntil::String('\n'.to_string())).unwrap();
214234 /// assert_eq!("Hello, miss!", &first_line);
@@ -230,6 +250,7 @@ impl NBReader {
230250
231251 loop {
232252 self . read_into_buffer ( ) ?;
253+
233254 if let Some ( tuple_pos) = find ( needle, & self . buffer , self . eof ) {
234255 let first = self . buffer . drain ( ..tuple_pos. 0 ) . collect ( ) ;
235256 let second = self . buffer . drain ( ..tuple_pos. 1 - tuple_pos. 0 ) . collect ( ) ;
@@ -287,7 +308,7 @@ mod tests {
287308 #[ test]
288309 fn test_expect_melon ( ) {
289310 let f = io:: Cursor :: new ( "a melon\r \n " ) ;
290- let mut r = NBReader :: new ( f, None ) ;
311+ let mut r = NBReader :: new ( f, Options :: default ( ) ) ;
291312 assert_eq ! (
292313 ( "a melon" . to_string( ) , "\r \n " . to_string( ) ) ,
293314 r. read_until( & ReadUntil :: String ( "\r \n " . to_string( ) ) )
@@ -304,7 +325,7 @@ mod tests {
304325 #[ test]
305326 fn test_regex ( ) {
306327 let f = io:: Cursor :: new ( "2014-03-15" ) ;
307- let mut r = NBReader :: new ( f, None ) ;
328+ let mut r = NBReader :: new ( f, Options :: default ( ) ) ;
308329 let re = Regex :: new ( r"^\d{4}-\d{2}-\d{2}$" ) . unwrap ( ) ;
309330 assert_eq ! (
310331 ( "" . to_string( ) , "2014-03-15" . to_string( ) ) ,
@@ -316,7 +337,7 @@ mod tests {
316337 #[ test]
317338 fn test_regex2 ( ) {
318339 let f = io:: Cursor :: new ( "2014-03-15" ) ;
319- let mut r = NBReader :: new ( f, None ) ;
340+ let mut r = NBReader :: new ( f, Options :: default ( ) ) ;
320341 let re = Regex :: new ( r"-\d{2}-" ) . unwrap ( ) ;
321342 assert_eq ! (
322343 ( "2014" . to_string( ) , "-03-" . to_string( ) ) ,
@@ -328,7 +349,7 @@ mod tests {
328349 #[ test]
329350 fn test_nbytes ( ) {
330351 let f = io:: Cursor :: new ( "abcdef" ) ;
331- let mut r = NBReader :: new ( f, None ) ;
352+ let mut r = NBReader :: new ( f, Options :: default ( ) ) ;
332353 assert_eq ! (
333354 ( "" . to_string( ) , "ab" . to_string( ) ) ,
334355 r. read_until( & ReadUntil :: NBytes ( 2 ) ) . expect( "2 bytes" )
@@ -346,7 +367,7 @@ mod tests {
346367 #[ test]
347368 fn test_any_with_multiple_possible_matches ( ) {
348369 let f = io:: Cursor :: new ( "zero one two three four five" ) ;
349- let mut r = NBReader :: new ( f, None ) ;
370+ let mut r = NBReader :: new ( f, Options :: default ( ) ) ;
350371
351372 let result = r
352373 . read_until ( & ReadUntil :: Any ( vec ! [
@@ -361,7 +382,7 @@ mod tests {
361382 #[ test]
362383 fn test_any_with_same_start_different_length ( ) {
363384 let f = io:: Cursor :: new ( "hi hello" ) ;
364- let mut r = NBReader :: new ( f, None ) ;
385+ let mut r = NBReader :: new ( f, Options :: default ( ) ) ;
365386
366387 let result = r
367388 . read_until ( & ReadUntil :: Any ( vec ! [
@@ -376,18 +397,52 @@ mod tests {
376397 #[ test]
377398 fn test_eof ( ) {
378399 let f = io:: Cursor :: new ( "lorem ipsum dolor sit amet" ) ;
379- let mut r = NBReader :: new ( f, None ) ;
400+ let mut r = NBReader :: new ( f, Options :: default ( ) ) ;
380401 r. read_until ( & ReadUntil :: NBytes ( 2 ) ) . expect ( "2 bytes" ) ;
381402 assert_eq ! (
382403 ( "" . to_string( ) , "rem ipsum dolor sit amet" . to_string( ) ) ,
383404 r. read_until( & ReadUntil :: EOF ) . expect( "reading until EOF" )
384405 ) ;
385406 }
386407
408+ #[ test]
409+ fn test_skip_partial_ansi_code ( ) {
410+ let f = io:: Cursor :: new ( "\x1b [31;1;4mHello\x1b [1" ) ;
411+ let mut r = NBReader :: new (
412+ f,
413+ Options {
414+ timeout_ms : None ,
415+ strip_ansi_escape_codes : true ,
416+ } ,
417+ ) ;
418+ let bytes = r
419+ . read_until ( & ReadUntil :: String ( "Hello" . to_string ( ) ) )
420+ . unwrap ( ) ;
421+ assert_eq ! ( bytes, ( "" . to_string( ) , "Hello" . to_string( ) ) ) ;
422+ assert_eq ! ( None , r. try_read( ) ) ;
423+ }
424+
425+ #[ test]
426+ fn test_skip_ansi_codes ( ) {
427+ let f = io:: Cursor :: new ( "\x1b [31;1;4mHello\x1b [0m" ) ;
428+ let mut r = NBReader :: new (
429+ f,
430+ Options {
431+ timeout_ms : None ,
432+ strip_ansi_escape_codes : true ,
433+ } ,
434+ ) ;
435+ let bytes = r
436+ . read_until ( & ReadUntil :: String ( "Hello" . to_string ( ) ) )
437+ . unwrap ( ) ;
438+ assert_eq ! ( bytes, ( "" . to_string( ) , "Hello" . to_string( ) ) ) ;
439+ assert_eq ! ( None , r. try_read( ) ) ;
440+ }
441+
387442 #[ test]
388443 fn test_try_read ( ) {
389444 let f = io:: Cursor :: new ( "lorem" ) ;
390- let mut r = NBReader :: new ( f, None ) ;
445+ let mut r = NBReader :: new ( f, Options :: default ( ) ) ;
391446 let bytes = r. read_until ( & ReadUntil :: NBytes ( 4 ) ) . unwrap ( ) ;
392447 assert ! ( bytes. 0 . is_empty( ) ) ;
393448 assert_eq ! ( bytes. 1 , "lore" ) ;
0 commit comments