#![warn(clippy::pedantic)]
use clap::builder::NonEmptyStringValueParser;
use clap::{ArgGroup, Parser};
use std::io;
use std::process::Command;
use std::str;
use xstream_util::{Limiting, Rotating};
fn unescape_delimiter(char_ref: impl AsRef<str>) -> String {
let char_str = char_ref.as_ref();
let mut res = String::with_capacity(char_str.len());
let mut chars = char_str.chars();
while let Some(c) = chars.next() {
match c {
'\\' => {
let next = match chars.next() {
None => '/',
Some('0') => '\0',
Some('a') => '\u{07}',
Some('b') => '\u{08}',
Some('v') => '\u{0B}',
Some('f') => '\u{0C}',
Some('n') => '\n',
Some('r') => '\r',
Some('t') => '\t',
Some('e' | 'E') => '\u{1B}',
Some('\\') => '\\',
Some(c) => {
res.push('\\');
c
}
};
res.push(next);
}
_ => res.push(c),
};
}
res
}
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
#[clap(group(ArgGroup::new("delim").arg("null").conflicts_with("delimiter")))]
struct Cli {
#[clap(short, long, value_parser = NonEmptyStringValueParser::new(), default_value = "\\n")]
delimiter: String,
#[clap(short = '0', long)]
null: bool,
#[clap(short, long)]
write_delimiter: Option<String>,
#[clap(short, long, value_parser, default_value_t = 1)]
parallel: usize,
#[clap(short, long)]
reuse: bool,
#[clap(value_parser = NonEmptyStringValueParser::new())]
command: String,
#[clap(value_parser)]
args: Vec<String>,
}
fn main() {
let args = Cli::parse();
let delim = if args.null {
"\0".to_owned()
} else {
unescape_delimiter(args.delimiter)
};
let write_delim = args.write_delimiter.map(unescape_delimiter);
let mut command = Command::new(args.command);
command.args(args.args);
if args.reuse {
let mut pool = Rotating::new(command, args.parallel);
xstream_util::xstream(&mut pool, &mut io::stdin().lock(), &delim, &write_delim).unwrap();
} else {
let mut pool = Limiting::new(command, args.parallel);
xstream_util::xstream(&mut pool, &mut io::stdin().lock(), &delim, &write_delim).unwrap();
}
}
#[cfg(test)]
mod escape_tests {
use super::unescape_delimiter;
#[test]
fn parse_null_escape() {
assert_eq!(unescape_delimiter(r"\0"), "\0");
}
#[test]
fn parse_newline_escape() {
assert_eq!(unescape_delimiter(r"\n"), "\n");
}
#[test]
fn parse_newline_raw() {
assert_eq!(unescape_delimiter("\n"), "\n");
}
#[test]
fn parse_extra_space() {
assert_eq!(unescape_delimiter("\n "), "\n ");
}
}
#[cfg(test)]
mod cli_tests {
use super::Cli;
use clap::CommandFactory;
#[test]
fn test_cli() {
Cli::command().debug_assert()
}
}