Skip to content

Commit

Permalink
Merge #59
Browse files Browse the repository at this point in the history
59: Replace error-chain with thiserror r=matthiasbeyer a=matthiasbeyer

Replaces the abandoned `error-chain` crate with `thiserror`, but does not clean up any `expect()` or `unwrap()` calls!



Co-authored-by: Matthias Beyer <[email protected]>
  • Loading branch information
bors[bot] and matthiasbeyer authored Sep 12, 2022
2 parents 9e7e5f9 + cb0a305 commit d89b7ef
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 129 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ readme = "README.md"
[dependencies]
nix = "0.14"
regex = "1"
error-chain = "0.12"
tempfile = "3"
thiserror = "1.0.34"

[badges]
maintenance = { status = "passively-maintained" }
4 changes: 2 additions & 2 deletions examples/bash.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
extern crate rexpect;
use rexpect::errors::*;
use rexpect::error::Error;
use rexpect::spawn_bash;

fn run() -> Result<()> {
fn run() -> Result<(), Error> {
let mut p = spawn_bash(Some(1000))?;
p.execute("ping 8.8.8.8", "bytes")?;
p.send_control('z')?;
Expand Down
4 changes: 2 additions & 2 deletions examples/bash_read.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
extern crate rexpect;
use rexpect::errors::*;
use rexpect::error::Error;
use rexpect::spawn_bash;

fn run() -> Result<()> {
fn run() -> Result<(), Error> {
let mut p = spawn_bash(Some(2000))?;

// case 1: wait until program is done
Expand Down
4 changes: 2 additions & 2 deletions examples/exit_code.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
extern crate rexpect;

use rexpect::errors::*;
use rexpect::error::Error;
use rexpect::process::wait;
use rexpect::spawn;

/// The following code emits:
/// cat exited with code 0, all good!
/// cat exited with code 1
/// Output (stdout and stderr): cat: /this/does/not/exist: No such file or directory
fn exit_code_fun() -> Result<()> {
fn exit_code_fun() -> Result<(), Error> {
let p = spawn("cat /etc/passwd", Some(2000))?;
match p.process.wait() {
Ok(wait::WaitStatus::Exited(_, 0)) => println!("cat exited with code 0, all good!"),
Expand Down
4 changes: 2 additions & 2 deletions examples/ftp.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
extern crate rexpect;

use rexpect::errors::*;
use rexpect::error::Error;
use rexpect::spawn;

fn do_ftp() -> Result<()> {
fn do_ftp() -> Result<(), Error> {
let mut p = spawn("ftp speedtest.tele2.net", Some(2000))?;
p.exp_regex("Name \\(.*\\):")?;
p.send_line("anonymous")?;
Expand Down
6 changes: 3 additions & 3 deletions examples/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
extern crate rexpect;

use rexpect::errors::*;
use rexpect::error::Error;
use rexpect::session::PtyReplSession;
use rexpect::spawn;

fn ed_session() -> Result<PtyReplSession> {
fn ed_session() -> Result<PtyReplSession, Error> {
let mut ed = PtyReplSession {
// for `echo_on` you need to figure that out by trial and error.
// For bash and python repl it is false
Expand All @@ -25,7 +25,7 @@ fn ed_session() -> Result<PtyReplSession> {
Ok(ed)
}

fn do_ed_repl() -> Result<()> {
fn do_ed_repl() -> Result<(), Error> {
let mut ed = ed_session()?;
ed.send_line("a")?;
ed.send_line("ed is the best editor evar")?;
Expand Down
39 changes: 39 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::time;

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("EOF (End of File): Expected {} but got EOF after reading \"{}\" process terminated with {:?}", .expected, .got, .exit_code.as_ref().unwrap_or(&"unknown".to_string()))]
EOF {
expected: String,
got: String,
exit_code: Option<String>,
},

#[error("PipeError")]
BrokenPipe,

#[error("Timeout Error: Expected {} but got \"{}\" (after waiting {} ms)", .expected, .got, (.timeout.as_secs() * 1000) as u32 + .timeout.subsec_millis())]
Timeout {
expected: String,
got: String,
timeout: time::Duration,
},

#[error("The provided program name is empty.")]
EmptyProgramName,

#[error(transparent)]
Nix(#[from] nix::Error),

#[error(transparent)]
Io(#[from] std::io::Error),

#[error("Did not understand Ctrl-{}", .0)]
SendContolError(char),

#[error("Failed to send via MPSC channel")]
MpscSendError,

#[error(transparent)]
Regex(#[from] regex::Error),
}
37 changes: 3 additions & 34 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
//! extern crate rexpect;
//!
//! use rexpect::spawn;
//! use rexpect::errors::*;
//! use rexpect::error::*;
//!
//! fn do_ftp() -> Result<()> {
//! let mut p = spawn("ftp speedtest.tele2.net", Some(2000))?;
Expand Down Expand Up @@ -55,7 +55,7 @@
//! ```no_run
//! extern crate rexpect;
//! use rexpect::spawn_bash;
//! use rexpect::errors::*;
//! use rexpect::error::*;
//!
//!
//! fn run() -> Result<()> {
Expand All @@ -78,41 +78,10 @@
//!
//! ```
pub mod error;
pub mod process;
pub mod reader;
pub mod session;

pub use reader::ReadUntil;
pub use session::{spawn, spawn_bash, spawn_python, spawn_stream};

pub mod errors {
use std::time;
// Create the Error, ErrorKind, ResultExt, and Result types
error_chain::error_chain! {
errors {
EOF(expected:String, got:String, exit_code:Option<String>) {
description("End of filestream (usually stdout) occurred, most probably\
because the process terminated")
display("EOF (End of File): Expected {} but got EOF after reading \"{}\", \
process terminated with {:?}", expected, got,
exit_code.as_ref()
.unwrap_or(& "unknown".to_string()))
}
BrokenPipe {
description("The pipe to the process is broken. Most probably because\
the process died.")
display("PipeError")
}
Timeout(expected:String, got:String, timeout:time::Duration) {
description("The process didn't end within the given timeout")
display("Timeout Error: Expected {} but got \"{}\" (after waiting {} ms)",
expected, got, (timeout.as_secs() * 1000) as u32
+ timeout.subsec_millis())
}
EmptyProgramName {
description("The provided program name is empty.")
display("EmptyProgramName")
}
}
}
}
27 changes: 13 additions & 14 deletions src/process.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Start a process via pty
use crate::errors::*;
use crate::error::Error;
use nix;
use nix::fcntl::{open, OFlag};
use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
Expand All @@ -13,7 +13,7 @@ use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::{thread, time}; // load error-chain
use std::{thread, time};

/// Start a process in a forked tty so you can interact with it the same as you would
/// within a terminal
Expand Down Expand Up @@ -87,7 +87,7 @@ fn ptsname_r(fd: &PtyMaster) -> nix::Result<String> {

impl PtyProcess {
/// Start a process in a forked pty
pub fn new(mut command: Command) -> Result<Self> {
pub fn new(mut command: Command) -> Result<Self, Error> {
|| -> nix::Result<Self> {
// Open a new PTY master
let master_fd = posix_openpt(OFlag::O_RDWR)?;
Expand Down Expand Up @@ -128,7 +128,7 @@ impl PtyProcess {
}),
}
}()
.chain_err(|| format!("could not execute {:?}", command))
.map_err(Error::from)
}

/// Get handle to pty fork for reading/writing
Expand Down Expand Up @@ -177,19 +177,18 @@ impl PtyProcess {

/// Wait until process has exited. This is a blocking call.
/// If the process doesn't terminate this will block forever.
pub fn wait(&self) -> Result<wait::WaitStatus> {
wait::waitpid(self.child_pid, None).chain_err(|| "wait: cannot read status")
pub fn wait(&self) -> Result<wait::WaitStatus, Error> {
wait::waitpid(self.child_pid, None).map_err(Error::from)
}

/// Regularly exit the process, this method is blocking until the process is dead
pub fn exit(&mut self) -> Result<wait::WaitStatus> {
self.kill(signal::SIGTERM)
pub fn exit(&mut self) -> Result<wait::WaitStatus, Error> {
self.kill(signal::SIGTERM).map_err(Error::from)
}

/// Non-blocking variant of `kill()` (doesn't wait for process to be killed)
pub fn signal(&mut self, sig: signal::Signal) -> Result<()> {
signal::kill(self.child_pid, sig).chain_err(|| "failed to send signal to process")?;
Ok(())
pub fn signal(&mut self, sig: signal::Signal) -> Result<(), Error> {
signal::kill(self.child_pid, sig).map_err(Error::from)
}

/// Kill the process with a specific signal. This method blocks, until the process is dead
Expand All @@ -200,7 +199,7 @@ impl PtyProcess {
///
/// if `kill_timeout` is set and a repeated sending of signal does not result in the process
/// being killed, then `kill -9` is sent after the `kill_timeout` duration has elapsed.
pub fn kill(&mut self, sig: signal::Signal) -> Result<wait::WaitStatus> {
pub fn kill(&mut self, sig: signal::Signal) -> Result<wait::WaitStatus, Error> {
let start = time::Instant::now();
loop {
match signal::kill(self.child_pid, sig) {
Expand All @@ -209,7 +208,7 @@ impl PtyProcess {
Err(nix::Error::Sys(nix::errno::Errno::ESRCH)) => {
return Ok(wait::WaitStatus::Exited(Pid::from_raw(0), 0))
}
Err(e) => return Err(format!("kill resulted in error: {:?}", e).into()),
Err(e) => return Err(Error::from(e)),
}

match self.status() {
Expand All @@ -219,7 +218,7 @@ impl PtyProcess {
// kill -9 if timout is reached
if let Some(timeout) = self.kill_timeout {
if start.elapsed() > timeout {
signal::kill(self.child_pid, signal::Signal::SIGKILL).chain_err(|| "")?
signal::kill(self.child_pid, signal::Signal::SIGKILL).map_err(Error::from)?
}
}
}
Expand Down
35 changes: 20 additions & 15 deletions src/reader.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Unblocking reader which supports waiting for strings/regexes and EOF to be present
use crate::errors::*; // load error-chain
use crate::error::Error;
pub use regex::Regex;
use std::io::prelude::*;
use std::io::{self, BufReader};
Expand Down Expand Up @@ -120,22 +120,23 @@ impl NBReader {

// spawn a thread which reads one char and sends it to tx
thread::spawn(move || {
let _ = || -> Result<()> {
let _ = || -> Result<(), Error> {
let mut reader = BufReader::new(f);
let mut byte = [0u8];
loop {
match reader.read(&mut byte) {
Ok(0) => {
tx.send(Ok(PipedChar::EOF)).chain_err(|| "cannot send")?;
tx.send(Ok(PipedChar::EOF))
.map_err(|_| Error::MpscSendError)?;
break;
}
Ok(_) => {
tx.send(Ok(PipedChar::Char(byte[0])))
.chain_err(|| "cannot send")?;
.map_err(|_| Error::MpscSendError)?;
}
Err(error) => {
tx.send(Err(PipeError::IO(error)))
.chain_err(|| "cannot send")?;
.map_err(|_| Error::MpscSendError)?;
}
}
}
Expand All @@ -155,7 +156,7 @@ impl NBReader {
}

/// reads all available chars from the read channel and stores them in self.buffer
fn read_into_buffer(&mut self) -> Result<()> {
fn read_into_buffer(&mut self) -> Result<(), Error> {
if self.eof {
return Ok(());
}
Expand Down Expand Up @@ -222,7 +223,7 @@ impl NBReader {
/// assert_eq!("?", &until_end);
/// ```
///
pub fn read_until(&mut self, needle: &ReadUntil) -> Result<(String, String)> {
pub fn read_until(&mut self, needle: &ReadUntil) -> Result<(String, String), Error> {
let start = time::Instant::now();

loop {
Expand All @@ -237,22 +238,26 @@ impl NBReader {
// we don't know the reason of eof yet, so we provide an empty string
// this will be filled out in session::exp()
if self.eof {
return Err(ErrorKind::EOF(needle.to_string(), self.buffer.clone(), None).into());
return Err(Error::EOF {
expected: needle.to_string(),
got: self.buffer.clone(),
exit_code: None,
});
}

// ran into timeout
if let Some(timeout) = self.timeout {
if start.elapsed() > timeout {
return Err(ErrorKind::Timeout(
needle.to_string(),
self.buffer
return Err(Error::Timeout {
expected: needle.to_string(),
got: self
.buffer
.clone()
.replace('\n', "`\\n`\n")
.replace('\r', "`\\r`")
.replace('\u{1b}', "`^`"),
timeout,
)
.into());
});
}
}
// nothing matched: wait a little
Expand Down Expand Up @@ -289,8 +294,8 @@ mod tests {
// check for EOF
match r.read_until(&ReadUntil::NBytes(10)) {
Ok(_) => panic!(),
Err(Error(ErrorKind::EOF(_, _, _), _)) => {}
Err(Error(_, _)) => panic!(),
Err(Error::EOF { .. }) => {}
Err(_) => panic!(),
}
}

Expand Down
Loading

0 comments on commit d89b7ef

Please sign in to comment.