Skip to content

Replace error-chain with thiserror #59

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

Merged
merged 4 commits into from
Sep 12, 2022
Merged
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.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