Skip to content

make match_any also return index of matched needle #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"]
edition = "2018"
repository = "https://github.com/philippkeller/rexpect"
Expand All @@ -20,4 +20,4 @@ error-chain = "0.12"
tempfile = "3"

[badges]
travis-ci = { repository = "philippkeller/rexpect" }
travis-ci = { repository = "philippkeller/rexpect" }
1 change: 0 additions & 1 deletion src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
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
Expand Down
58 changes: 54 additions & 4 deletions src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ pub fn find(needle: &ReadUntil, buffer: &str, eof: bool) -> Option<(usize, usize
}
}

pub fn find_any(needle: &Vec<ReadUntil>, buffer: &str, eof: bool) -> Option<(usize, usize, usize)> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn find_any(needle: &Vec<ReadUntil>, buffer: &str, eof: bool) -> Option<(usize, usize, usize)> {
pub fn find_any(needle: &[ReadUntil], 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.
Expand Down Expand Up @@ -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();
Comment on lines +236 to +237
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might it would be better to move it in read_until_tuple_pos to remove a duplication in read_until_any.
In this case read_until_tuple_pos would return Result<(String, String, usize)>. And in read_until_any would be no need? Since read_until_tuple_pos would return exactly what it does now.
*after all the function could be renamed?

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<ReadUntil>) -> 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
Comment on lines +260 to +261
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
/// Read until needle is found and return
}
/// 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
Expand Down
19 changes: 15 additions & 4 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ReadUntil>) -> 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")
Expand Down Expand Up @@ -179,8 +190,8 @@ impl PtySession {
/// # }().expect("test failed");
/// # }
/// ```
pub fn exp_any(&mut self, needles: Vec<ReadUntil>) -> Result<(String, String)> {
self.exp(&ReadUntil::Any(needles))
pub fn exp_any(&mut self, needles: Vec<ReadUntil>) -> Result<(String, String, usize)> {
self.exp_any_wrapper(needles)
}
}

Expand Down Expand Up @@ -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(())
Expand Down