// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Working with processes. #![stable(feature = "process", since = "1.0.0")] use prelude::v1::*; use io::prelude::*; use ffi::OsStr; use fmt; use io; use path::Path; use str; use sys::pipe::{read2, AnonPipe}; use sys::process as imp; use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; /// Representation of a running or exited child process. /// /// This structure is used to represent and manage child processes. A child /// process is created via the `Command` struct, which configures the spawning /// process and can itself be constructed using a builder-style interface. /// /// # Examples /// /// ```should_panic /// use std::process::Command; /// /// let mut child = Command::new("/bin/cat") /// .arg("file.txt") /// .spawn() /// .unwrap_or_else(|e| { panic!("failed to execute child: {}", e) }); /// /// let ecode = child.wait() /// .unwrap_or_else(|e| { panic!("failed to wait on child: {}", e) }); /// /// assert!(ecode.success()); /// ``` /// /// # Note /// /// Take note that there is no implementation of /// [`Drop`](../../core/ops/trait.Drop.html) for child processes, so if you /// do not ensure the `Child` has exited then it will continue to run, even /// after the `Child` handle to the child process has gone out of scope. /// /// Calling `wait` (or other functions that wrap around it) will make the /// parent process wait until the child has actually exited before continuing. #[stable(feature = "process", since = "1.0.0")] pub struct Child { handle: imp::Process, /// The handle for writing to the child's stdin, if it has been captured #[stable(feature = "process", since = "1.0.0")] pub stdin: Option, /// The handle for reading from the child's stdout, if it has been captured #[stable(feature = "process", since = "1.0.0")] pub stdout: Option, /// The handle for reading from the child's stderr, if it has been captured #[stable(feature = "process", since = "1.0.0")] pub stderr: Option, } impl AsInner for Child { fn as_inner(&self) -> &imp::Process { &self.handle } } impl FromInner<(imp::Process, imp::StdioPipes)> for Child { fn from_inner((handle, io): (imp::Process, imp::StdioPipes)) -> Child { Child { handle: handle, stdin: io.stdin.map(ChildStdin::from_inner), stdout: io.stdout.map(ChildStdout::from_inner), stderr: io.stderr.map(ChildStderr::from_inner), } } } impl IntoInner for Child { fn into_inner(self) -> imp::Process { self.handle } } /// A handle to a child process's stdin #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdin { inner: AnonPipe } #[stable(feature = "process", since = "1.0.0")] impl Write for ChildStdin { fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.write(buf) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl AsInner for ChildStdin { fn as_inner(&self) -> &AnonPipe { &self.inner } } impl IntoInner for ChildStdin { fn into_inner(self) -> AnonPipe { self.inner } } impl FromInner for ChildStdin { fn from_inner(pipe: AnonPipe) -> ChildStdin { ChildStdin { inner: pipe } } } /// A handle to a child process's stdout #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdout { inner: AnonPipe } #[stable(feature = "process", since = "1.0.0")] impl Read for ChildStdout { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.read(buf) } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { self.inner.read_to_end(buf) } } impl AsInner for ChildStdout { fn as_inner(&self) -> &AnonPipe { &self.inner } } impl IntoInner for ChildStdout { fn into_inner(self) -> AnonPipe { self.inner } } impl FromInner for ChildStdout { fn from_inner(pipe: AnonPipe) -> ChildStdout { ChildStdout { inner: pipe } } } /// A handle to a child process's stderr #[stable(feature = "process", since = "1.0.0")] pub struct ChildStderr { inner: AnonPipe } #[stable(feature = "process", since = "1.0.0")] impl Read for ChildStderr { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.read(buf) } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { self.inner.read_to_end(buf) } } impl AsInner for ChildStderr { fn as_inner(&self) -> &AnonPipe { &self.inner } } impl IntoInner for ChildStderr { fn into_inner(self) -> AnonPipe { self.inner } } impl FromInner for ChildStderr { fn from_inner(pipe: AnonPipe) -> ChildStderr { ChildStderr { inner: pipe } } } /// The `Command` type acts as a process builder, providing fine-grained control /// over how a new process should be spawned. A default configuration can be /// generated using `Command::new(program)`, where `program` gives a path to the /// program to be executed. Additional builder methods allow the configuration /// to be changed (for example, by adding arguments) prior to spawning: /// /// ``` /// use std::process::Command; /// /// let output = Command::new("sh") /// .arg("-c") /// .arg("echo hello") /// .output() /// .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) }); /// let hello = output.stdout; /// ``` #[stable(feature = "process", since = "1.0.0")] pub struct Command { inner: imp::Command, } impl Command { /// Constructs a new `Command` for launching the program at /// path `program`, with the following default configuration: /// /// * No arguments to the program /// * Inherit the current process's environment /// * Inherit the current process's working directory /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes for `output` /// /// Builder methods are provided to change these defaults and /// otherwise configure the process. #[stable(feature = "process", since = "1.0.0")] pub fn new>(program: S) -> Command { Command { inner: imp::Command::new(program.as_ref()) } } /// Add an argument to pass to the program. #[stable(feature = "process", since = "1.0.0")] pub fn arg>(&mut self, arg: S) -> &mut Command { self.inner.arg(arg.as_ref()); self } /// Add multiple arguments to pass to the program. #[stable(feature = "process", since = "1.0.0")] pub fn args>(&mut self, args: &[S]) -> &mut Command { for arg in args { self.arg(arg.as_ref()); } self } /// Inserts or updates an environment variable mapping. /// /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, /// and case-sensitive on all other platforms. #[stable(feature = "process", since = "1.0.0")] pub fn env(&mut self, key: K, val: V) -> &mut Command where K: AsRef, V: AsRef { self.inner.env(key.as_ref(), val.as_ref()); self } /// Removes an environment variable mapping. #[stable(feature = "process", since = "1.0.0")] pub fn env_remove>(&mut self, key: K) -> &mut Command { self.inner.env_remove(key.as_ref()); self } /// Clears the entire environment map for the child process. #[stable(feature = "process", since = "1.0.0")] pub fn env_clear(&mut self) -> &mut Command { self.inner.env_clear(); self } /// Sets the working directory for the child process. #[stable(feature = "process", since = "1.0.0")] pub fn current_dir>(&mut self, dir: P) -> &mut Command { self.inner.cwd(dir.as_ref().as_ref()); self } /// Configuration for the child process's stdin handle (file descriptor 0). #[stable(feature = "process", since = "1.0.0")] pub fn stdin(&mut self, cfg: Stdio) -> &mut Command { self.inner.stdin(cfg.0); self } /// Configuration for the child process's stdout handle (file descriptor 1). #[stable(feature = "process", since = "1.0.0")] pub fn stdout(&mut self, cfg: Stdio) -> &mut Command { self.inner.stdout(cfg.0); self } /// Configuration for the child process's stderr handle (file descriptor 2). #[stable(feature = "process", since = "1.0.0")] pub fn stderr(&mut self, cfg: Stdio) -> &mut Command { self.inner.stderr(cfg.0); self } /// Executes the command as a child process, returning a handle to it. /// /// By default, stdin, stdout and stderr are inherited from the parent. #[stable(feature = "process", since = "1.0.0")] pub fn spawn(&mut self) -> io::Result { self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner) } /// Executes the command as a child process, waiting for it to finish and /// collecting all of its output. /// /// By default, stdin, stdout and stderr are captured (and used to /// provide the resulting output). /// /// # Examples /// /// ``` /// use std::process::Command; /// let output = Command::new("cat").arg("foo.txt").output().unwrap_or_else(|e| { /// panic!("failed to execute process: {}", e) /// }); /// /// println!("status: {}", output.status); /// println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); /// println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn output(&mut self) -> io::Result { self.inner.spawn(imp::Stdio::MakePipe, false).map(Child::from_inner) .and_then(|p| p.wait_with_output()) } /// Executes a command as a child process, waiting for it to finish and /// collecting its exit status. /// /// By default, stdin, stdout and stderr are inherited from the parent. /// /// # Examples /// /// ``` /// use std::process::Command; /// /// let status = Command::new("ls").status().unwrap_or_else(|e| { /// panic!("failed to execute process: {}", e) /// }); /// /// println!("process exited with: {}", status); /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn status(&mut self) -> io::Result { self.inner.spawn(imp::Stdio::Inherit, false).map(Child::from_inner) .and_then(|mut p| p.wait()) } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Command { /// Format the program and arguments of a Command for display. Any /// non-utf8 data is lossily converted using the utf8 replacement /// character. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt(f) } } impl AsInner for Command { fn as_inner(&self) -> &imp::Command { &self.inner } } impl AsInnerMut for Command { fn as_inner_mut(&mut self) -> &mut imp::Command { &mut self.inner } } /// The output of a finished process. #[derive(PartialEq, Eq, Clone)] #[stable(feature = "process", since = "1.0.0")] pub struct Output { /// The status (exit code) of the process. #[stable(feature = "process", since = "1.0.0")] pub status: ExitStatus, /// The data that the process wrote to stdout. #[stable(feature = "process", since = "1.0.0")] pub stdout: Vec, /// The data that the process wrote to stderr. #[stable(feature = "process", since = "1.0.0")] pub stderr: Vec, } // If either stderr or stdout are valid utf8 strings it prints the valid // strings, otherwise it prints the byte sequence instead #[stable(feature = "process_output_debug", since = "1.7.0")] impl fmt::Debug for Output { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let stdout_utf8 = str::from_utf8(&self.stdout); let stdout_debug: &fmt::Debug = match stdout_utf8 { Ok(ref str) => str, Err(_) => &self.stdout }; let stderr_utf8 = str::from_utf8(&self.stderr); let stderr_debug: &fmt::Debug = match stderr_utf8 { Ok(ref str) => str, Err(_) => &self.stderr }; fmt.debug_struct("Output") .field("status", &self.status) .field("stdout", stdout_debug) .field("stderr", stderr_debug) .finish() } } /// Describes what to do with a standard I/O stream for a child process. #[stable(feature = "process", since = "1.0.0")] pub struct Stdio(imp::Stdio); impl Stdio { /// A new pipe should be arranged to connect the parent and child processes. #[stable(feature = "process", since = "1.0.0")] pub fn piped() -> Stdio { Stdio(imp::Stdio::MakePipe) } /// The child inherits from the corresponding parent descriptor. #[stable(feature = "process", since = "1.0.0")] pub fn inherit() -> Stdio { Stdio(imp::Stdio::Inherit) } /// This stream will be ignored. This is the equivalent of attaching the /// stream to `/dev/null` #[stable(feature = "process", since = "1.0.0")] pub fn null() -> Stdio { Stdio(imp::Stdio::Null) } } impl FromInner for Stdio { fn from_inner(inner: imp::Stdio) -> Stdio { Stdio(inner) } } /// Describes the result of a process after it has terminated. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[stable(feature = "process", since = "1.0.0")] pub struct ExitStatus(imp::ExitStatus); impl ExitStatus { /// Was termination successful? Signal termination not considered a success, /// and success is defined as a zero exit status. #[stable(feature = "process", since = "1.0.0")] pub fn success(&self) -> bool { self.0.success() } /// Returns the exit code of the process, if any. /// /// On Unix, this will return `None` if the process was terminated /// by a signal; `std::os::unix` provides an extension trait for /// extracting the signal and other details from the `ExitStatus`. #[stable(feature = "process", since = "1.0.0")] pub fn code(&self) -> Option { self.0.code() } } impl AsInner for ExitStatus { fn as_inner(&self) -> &imp::ExitStatus { &self.0 } } #[stable(feature = "process", since = "1.0.0")] impl fmt::Display for ExitStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } } impl Child { /// Forces the child to exit. This is equivalent to sending a /// SIGKILL on unix platforms. #[stable(feature = "process", since = "1.0.0")] pub fn kill(&mut self) -> io::Result<()> { self.handle.kill() } /// Returns the OS-assigned process identifier associated with this child. #[stable(feature = "process_id", since = "1.3.0")] pub fn id(&self) -> u32 { self.handle.id() } /// Waits for the child to exit completely, returning the status that it /// exited with. This function will continue to have the same return value /// after it has been called at least once. /// /// The stdin handle to the child process, if any, will be closed /// before waiting. This helps avoid deadlock: it ensures that the /// child does not block waiting for input from the parent, while /// the parent waits for the child to exit. #[stable(feature = "process", since = "1.0.0")] pub fn wait(&mut self) -> io::Result { drop(self.stdin.take()); self.handle.wait().map(ExitStatus) } /// Simultaneously waits for the child to exit and collect all remaining /// output on the stdout/stderr handles, returning an `Output` /// instance. /// /// The stdin handle to the child process, if any, will be closed /// before waiting. This helps avoid deadlock: it ensures that the /// child does not block waiting for input from the parent, while /// the parent waits for the child to exit. #[stable(feature = "process", since = "1.0.0")] pub fn wait_with_output(mut self) -> io::Result { drop(self.stdin.take()); let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); match (self.stdout.take(), self.stderr.take()) { (None, None) => {} (Some(mut out), None) => { let res = out.read_to_end(&mut stdout); res.unwrap(); } (None, Some(mut err)) => { let res = err.read_to_end(&mut stderr); res.unwrap(); } (Some(out), Some(err)) => { let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); res.unwrap(); } } let status = try!(self.wait()); Ok(Output { status: status, stdout: stdout, stderr: stderr, }) } } /// Terminates the current process with the specified exit code. /// /// This function will never return and will immediately terminate the current /// process. The exit code is passed through to the underlying OS and will be /// available for consumption by another process. /// /// Note that because this function never returns, and that it terminates the /// process, no destructors on the current stack or any other thread's stack /// will be run. If a clean shutdown is needed it is recommended to only call /// this function at a known point where there are no more destructors left /// to run. #[stable(feature = "rust1", since = "1.0.0")] pub fn exit(code: i32) -> ! { ::sys_common::cleanup(); ::sys::os::exit(code) } #[cfg(test)] mod tests { use prelude::v1::*; use io::prelude::*; use io::ErrorKind; use str; use super::{Command, Output, Stdio}; // FIXME(#10380) these tests should not all be ignored on android. #[test] #[cfg_attr(target_os = "android", ignore)] fn smoke() { let p = Command::new("true").spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); assert!(p.wait().unwrap().success()); } #[test] #[cfg_attr(target_os = "android", ignore)] fn smoke_failure() { match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { Ok(..) => panic!(), Err(..) => {} } } #[test] #[cfg_attr(target_os = "android", ignore)] fn exit_reported_right() { let p = Command::new("false").spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); assert!(p.wait().unwrap().code() == Some(1)); drop(p.wait()); } #[test] #[cfg(unix)] #[cfg_attr(target_os = "android", ignore)] fn signal_reported_right() { use os::unix::process::ExitStatusExt; let mut p = Command::new("/bin/sh") .arg("-c").arg("read a") .stdin(Stdio::piped()) .spawn().unwrap(); p.kill().unwrap(); match p.wait().unwrap().signal() { Some(9) => {}, result => panic!("not terminated by signal 9 (instead, {:?})", result), } } pub fn run_output(mut cmd: Command) -> String { let p = cmd.spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); assert!(p.stdout.is_some()); let mut ret = String::new(); p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap(); assert!(p.wait().unwrap().success()); return ret; } #[test] #[cfg_attr(target_os = "android", ignore)] fn stdout_works() { let mut cmd = Command::new("echo"); cmd.arg("foobar").stdout(Stdio::piped()); assert_eq!(run_output(cmd), "foobar\n"); } #[test] #[cfg_attr(any(windows, target_os = "android"), ignore)] fn set_current_dir_works() { let mut cmd = Command::new("/bin/sh"); cmd.arg("-c").arg("pwd") .current_dir("/") .stdout(Stdio::piped()); assert_eq!(run_output(cmd), "/\n"); } #[test] #[cfg_attr(any(windows, target_os = "android"), ignore)] fn stdin_works() { let mut p = Command::new("/bin/sh") .arg("-c").arg("read line; echo $line") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn().unwrap(); p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap(); drop(p.stdin.take()); let mut out = String::new(); p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap(); assert!(p.wait().unwrap().success()); assert_eq!(out, "foobar\n"); } #[test] #[cfg_attr(target_os = "android", ignore)] #[cfg(unix)] fn uid_works() { use os::unix::prelude::*; use libc; let mut p = Command::new("/bin/sh") .arg("-c").arg("true") .uid(unsafe { libc::getuid() }) .gid(unsafe { libc::getgid() }) .spawn().unwrap(); assert!(p.wait().unwrap().success()); } #[test] #[cfg_attr(target_os = "android", ignore)] #[cfg(unix)] fn uid_to_root_fails() { use os::unix::prelude::*; use libc; // if we're already root, this isn't a valid test. Most of the bots run // as non-root though (android is an exception). if unsafe { libc::getuid() == 0 } { return } assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err()); } #[test] #[cfg_attr(target_os = "android", ignore)] fn test_process_status() { let mut status = Command::new("false").status().unwrap(); assert!(status.code() == Some(1)); status = Command::new("true").status().unwrap(); assert!(status.success()); } #[test] fn test_process_output_fail_to_start() { match Command::new("/no-binary-by-this-name-should-exist").output() { Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound), Ok(..) => panic!() } } #[test] #[cfg_attr(target_os = "android", ignore)] fn test_process_output_output() { let Output {status, stdout, stderr} = Command::new("echo").arg("hello").output().unwrap(); let output_str = str::from_utf8(&stdout).unwrap(); assert!(status.success()); assert_eq!(output_str.trim().to_string(), "hello"); assert_eq!(stderr, Vec::new()); } #[test] #[cfg_attr(target_os = "android", ignore)] fn test_process_output_error() { let Output {status, stdout, stderr} = Command::new("mkdir").arg(".").output().unwrap(); assert!(status.code() == Some(1)); assert_eq!(stdout, Vec::new()); assert!(!stderr.is_empty()); } #[test] #[cfg_attr(target_os = "android", ignore)] fn test_finish_once() { let mut prog = Command::new("false").spawn().unwrap(); assert!(prog.wait().unwrap().code() == Some(1)); } #[test] #[cfg_attr(target_os = "android", ignore)] fn test_finish_twice() { let mut prog = Command::new("false").spawn().unwrap(); assert!(prog.wait().unwrap().code() == Some(1)); assert!(prog.wait().unwrap().code() == Some(1)); } #[test] #[cfg_attr(target_os = "android", ignore)] fn test_wait_with_output_once() { let prog = Command::new("echo").arg("hello").stdout(Stdio::piped()) .spawn().unwrap(); let Output {status, stdout, stderr} = prog.wait_with_output().unwrap(); let output_str = str::from_utf8(&stdout).unwrap(); assert!(status.success()); assert_eq!(output_str.trim().to_string(), "hello"); assert_eq!(stderr, Vec::new()); } #[cfg(all(unix, not(target_os="android")))] pub fn env_cmd() -> Command { Command::new("env") } #[cfg(target_os="android")] pub fn env_cmd() -> Command { let mut cmd = Command::new("/system/bin/sh"); cmd.arg("-c").arg("set"); cmd } #[cfg(windows)] pub fn env_cmd() -> Command { let mut cmd = Command::new("cmd"); cmd.arg("/c").arg("set"); cmd } #[test] fn test_inherit_env() { use env; let result = env_cmd().output().unwrap(); let output = String::from_utf8(result.stdout).unwrap(); for (ref k, ref v) in env::vars() { // don't check android RANDOM variables if cfg!(target_os = "android") && *k == "RANDOM" { continue } // Windows has hidden environment variables whose names start with // equals signs (`=`). Those do not show up in the output of the // `set` command. assert!((cfg!(windows) && k.starts_with("=")) || k.starts_with("DYLD") || output.contains(&format!("{}={}", *k, *v)) || output.contains(&format!("{}='{}'", *k, *v)), "output doesn't contain `{}={}`\n{}", k, v, output); } } #[test] fn test_override_env() { use env; // In some build environments (such as chrooted Nix builds), `env` can // only be found in the explicitly-provided PATH env variable, not in // default places such as /bin or /usr/bin. So we need to pass through // PATH to our sub-process. let mut cmd = env_cmd(); cmd.env_clear().env("RUN_TEST_NEW_ENV", "123"); if let Some(p) = env::var_os("PATH") { cmd.env("PATH", &p); } let result = cmd.output().unwrap(); let output = String::from_utf8_lossy(&result.stdout).to_string(); assert!(output.contains("RUN_TEST_NEW_ENV=123"), "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); } #[test] fn test_add_to_env() { let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap(); let output = String::from_utf8_lossy(&result.stdout).to_string(); assert!(output.contains("RUN_TEST_NEW_ENV=123"), "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); } // Regression tests for #30858. #[test] fn test_interior_nul_in_progname_is_error() { match Command::new("has-some-\0\0s-inside").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), Ok(_) => panic!(), } } #[test] fn test_interior_nul_in_arg_is_error() { match Command::new("echo").arg("has-some-\0\0s-inside").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), Ok(_) => panic!(), } } #[test] fn test_interior_nul_in_args_is_error() { match Command::new("echo").args(&["has-some-\0\0s-inside"]).spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), Ok(_) => panic!(), } } #[test] fn test_interior_nul_in_current_dir_is_error() { match Command::new("echo").current_dir("has-some-\0\0s-inside").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), Ok(_) => panic!(), } } // Regression tests for #30862. #[test] fn test_interior_nul_in_env_key_is_error() { match env_cmd().env("has-some-\0\0s-inside", "value").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), Ok(_) => panic!(), } } #[test] fn test_interior_nul_in_env_value_is_error() { match env_cmd().env("key", "has-some-\0\0s-inside").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), Ok(_) => panic!(), } } }