#![forbid(unsafe_code)]
const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug)]
struct Error {
#[allow(dead_code)]
message: String,
}
impl Error {
fn new(message: &str) -> Error {
Error {
message: message.to_string(),
}
}
}
impl From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Self {
Error {
message: error.to_string(),
}
}
}
type Result<T> = std::result::Result<T, Error>;
struct LineExec {
command: String,
variable: String,
stdin: std::io::Stdin,
stdout: std::io::Stdout,
stderr: std::io::Stderr,
}
impl LineExec {
fn new() -> Self {
let args = LineExec::cmdline_args();
let variable = String::from(&args[1]);
let command = String::from(&args[2]);
LineExec {
command,
variable,
stdin: std::io::stdin(),
stdout: std::io::stdout(),
stderr: std::io::stderr(),
}
}
fn cmdline_args() -> Vec<String> {
let args: Vec<String> = std::env::args().collect();
if args.len() == 2 && args[1] == "--version" {
println!("{} v{}", &args[0], VERSION);
std::process::exit(0);
}
if args.len() != 3 {
eprintln!("Error: Wrong number of command-line arguments\n");
eprintln!("Usage: {} <variable-name> <command-to-execute>", &args[0]);
eprintln!("Example: ls | {} v 'echo $v'", &args[0]);
std::process::exit(1);
}
args
}
fn command(&self, buf: String) -> Result<std::process::Output> {
let command = self.command.replace(&format!("${}", self.variable), buf.trim());
Ok(std::process::Command::new("sh").arg("-c").arg(command).output()?)
}
fn exec(&self) -> Result<()> {
let mut stdin = self.stdin.lock();
let mut stdout = self.stdout.lock();
let mut stderr = self.stderr.lock();
let mut status_vec: Vec<std::process::ExitStatus> = Vec::new();
loop {
let mut buf = String::new();
let bytest_read = std::io::BufRead::read_line(&mut stdin, &mut buf)?;
if bytest_read == 0 {
break;
}
let command_output = self.command(buf)?;
std::io::Write::write_all(&mut stdout, &command_output.stdout)?;
if !command_output.status.success() {
std::io::Write::write_all(&mut stderr, &command_output.stderr)?;
}
status_vec.push(command_output.status);
}
if status_vec.into_iter().all(|s| s.success()) {
Ok(())
} else {
Err(Error::new("Error running at least one the line commands"))
}
}
}
fn main() -> ! {
LineExec::new()
.exec()
.map_err(|e| {
eprintln!("Error: {}", e.message);
std::process::exit(1);
})
.unwrap();
std::process::exit(0);
}