From 4bcdfbe1ea65a97e5297fb94238c494ff4b7055e Mon Sep 17 00:00:00 2001 From: Philipp Keller Date: Sun, 24 May 2020 11:10:06 +0200 Subject: [PATCH 1/2] make match_any also return index of matched needle. Public api stays the same except in exp_any which is additionally returning an index --- src/process.rs | 1 - src/reader.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++---- src/session.rs | 19 +++++++++++++---- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/process.rs b/src/process.rs index d45c8250..9e5091b1 100644 --- a/src/process.rs +++ b/src/process.rs @@ -71,7 +71,6 @@ use nix::pty::ptsname_r; /// based on https://blog.tarq.io/ptsname-on-osx-with-rust/ fn ptsname_r(fd: &PtyMaster) -> nix::Result { use std::ffi::CStr; - use std::os::unix::io::AsRawFd; use nix::libc::{ioctl, TIOCPTYGNAME}; // the buffer size on OSX is 128, defined by sys/ttycom.h diff --git a/src/reader.rs b/src/reader.rs index f479991b..7fd11230 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -93,6 +93,15 @@ pub fn find(needle: &ReadUntil, buffer: &str, eof: bool) -> Option<(usize, usize } } +pub fn find_any(needle: &Vec, buffer: &str, eof: bool) -> Option<(usize, usize, usize)> { + for (pos, read_until) in needle.iter().enumerate() { + if let Some((before, after)) = find(&read_until, buffer, eof) { + return Some((before, after, pos)); + } + } + None +} + /// Non blocking reader /// /// Typically you'd need that to check for output of a process without blocking your thread. @@ -222,14 +231,55 @@ impl NBReader { /// ``` /// pub fn read_until(&mut self, needle: &ReadUntil) -> Result<(String, String)> { + match self.read_until_tuple_pos(needle) { + Ok(tuple_pos) => { + let first = self.buffer.drain(..tuple_pos.0).collect(); + let second = self.buffer.drain(..tuple_pos.1 - tuple_pos.0).collect(); + Ok((first, second)) + }, + Err(e) => Err(e), + } + } + + /// Read until any needle is found (blocking!) and return tuple with: + /// 1. yet unread string until and without needle + /// 2. matched needle + /// 3. index of which needle is found + /// + /// this is the same function as read_until with the difference that the index + /// is also returned + pub fn read_until_any(&mut self, needle: Vec) -> Result<(String, String, usize)> { + match self.read_until_tuple_pos(&ReadUntil::Any(needle)) { + Ok(tuple_pos) => { + let first = self.buffer.drain(..tuple_pos.0).collect(); + let second = self.buffer.drain(..tuple_pos.1 - tuple_pos.0).collect(); + Ok((first, second, tuple_pos.2)) + }, + Err(e) => Err(e), + } + } + /// Read until needle is found and return + /// 1. yet unread string until and without needle + /// 2. matched needle + /// 3. index of which needle is found. This is only making sense for ReadUntil::Any + /// for the other types this is redundant but I didn't find any more elegant way + /// to solve this. + fn read_until_tuple_pos(&mut self, needle: &ReadUntil) -> Result<(usize, usize, usize)> { let start = time::Instant::now(); loop { self.read_into_buffer()?; - if let Some(tuple_pos) = find(needle, &self.buffer, self.eof) { - let first = self.buffer.drain(..tuple_pos.0).collect(); - let second = self.buffer.drain(..tuple_pos.1 - tuple_pos.0).collect(); - return Ok((first, second)); + match needle { + ReadUntil::Any(n) => { + if let Some(tuple_pos) = find_any(n, &self.buffer, self.eof) { + return Ok(tuple_pos) + } + } + needle => { + if let Some((before, after)) = find(needle, &self.buffer, self.eof) { + return Ok((before, after, 0)) + } + } } // reached end of stream and didn't match -> error diff --git a/src/session.rs b/src/session.rs index e34e9b73..80f372fe 100644 --- a/src/session.rs +++ b/src/session.rs @@ -99,6 +99,17 @@ impl PtySession { } } + // wrapper around reader::read_until to give more context for errors + fn exp_any_wrapper(&mut self, needle: Vec) -> Result<(String, String, usize)> { + match self.reader.read_until_any(needle) { + Ok(s) => Ok(s), + Err(Error(ErrorKind::EOF(expected, got, _), _)) => { + Err(ErrorKind::EOF(expected, got, self.process.status()).into()) + } + Err(e) => Err(e), + } + } + /// Make sure all bytes written via `send()` are sent to the process pub fn flush(&mut self) -> Result<()> { self.writer.flush().chain_err(|| "could not flush") @@ -179,8 +190,8 @@ impl PtySession { /// # }().expect("test failed"); /// # } /// ``` - pub fn exp_any(&mut self, needles: Vec) -> Result<(String, String)> { - self.exp(&ReadUntil::Any(needles)) + pub fn exp_any(&mut self, needles: Vec) -> Result<(String, String, usize)> { + self.exp_any_wrapper(needles) } } @@ -483,8 +494,8 @@ mod tests { || -> Result<()> { let mut p = spawn("cat", Some(1000)).expect("cannot run cat"); p.send_line("Hi")?; - match p.exp_any(vec![ReadUntil::NBytes(3), ReadUntil::String("Hi".to_string())]) { - Ok(s) => assert_eq!(("".to_string(), "Hi\r".to_string()), s), + match p.exp_any(vec![ReadUntil::String("Bye".to_string()), ReadUntil::String("Hi".to_string())]) { + Ok(s) => assert_eq!(("".to_string(), "Hi".to_string(), 1), s), Err(e) => assert!(false, format!("got error: {}", e)), } Ok(()) From f26b3dfa669e4ed173a139456d31b3ed7f2ed14f Mon Sep 17 00:00:00 2001 From: Philipp Keller Date: Sun, 24 May 2020 11:16:29 +0200 Subject: [PATCH 2/2] bump up version --- Cargo.lock | 2 +- Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5416a475..4500f613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,7 +204,7 @@ dependencies = [ [[package]] name = "rexpect" -version = "0.3.0" +version = "0.4.0" dependencies = [ "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 709ec3ae..1bececa5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Interact with unix processes/bash the same way as pexpect or Don libes expect does" name = "rexpect" -version = "0.3.0" +version = "0.4.0" authors = ["Philipp Keller "] edition = "2018" repository = "https://github.com/philippkeller/rexpect" @@ -20,4 +20,4 @@ error-chain = "0.12" tempfile = "3" [badges] -travis-ci = { repository = "philippkeller/rexpect" } \ No newline at end of file +travis-ci = { repository = "philippkeller/rexpect" }