use csv;
use crate::config::{Config, Delimiter};
use crate::util;
use crate::CliResult;
use clap::Parser;
#[derive(Parser, Debug)]
pub struct Args {
#[arg()]
pub mode: String,
#[arg()]
pub arg_input: Vec<String>,
#[arg(short = 'p', long = "pad")]
pub flag_pad: bool,
#[arg(short = 'o', long = "output", value_name = "file")]
pub flag_output: Option<String>,
#[arg(short = 'n', long = "no-headers")]
pub flag_no_headers: bool,
#[arg(short = 'd', long = "delimiter", value_name = "arg")]
pub flag_delimiter: Option<Delimiter>,
}
pub fn run(args: &Args) -> CliResult<()> {
match args.mode.as_str() {
"rows" => args.cat_rows(),
"columns" => args.cat_columns(),
_ => Err(crate::CliError::Other(format!(
"Unknown cat mode '{}'. Use 'rows' or 'columns'.",
args.mode
))),
}
}
impl Args {
fn configs(&self) -> CliResult<Vec<Config>> {
util::many_configs(&self.arg_input, self.flag_delimiter, self.flag_no_headers)
.map_err(From::from)
}
fn cat_rows(&self) -> CliResult<()> {
let mut row = csv::ByteRecord::new();
let mut wtr = Config::new(&self.flag_output).writer()?;
for (i, conf) in self.configs()?.into_iter().enumerate() {
let mut rdr = conf.reader()?;
if i == 0 {
conf.write_headers(&mut rdr, &mut wtr)?;
}
while rdr.read_byte_record(&mut row)? {
wtr.write_byte_record(&row)?;
}
}
wtr.flush().map_err(From::from)
}
fn cat_columns(&self) -> CliResult<()> {
let mut wtr = Config::new(&self.flag_output).writer()?;
let mut rdrs = self
.configs()?
.into_iter()
.map(|conf| conf.no_headers(true).reader())
.collect::<Result<Vec<_>, _>>()?;
let mut lengths = vec![];
for rdr in &mut rdrs {
lengths.push(rdr.byte_headers()?.len());
}
let mut iters = rdrs
.iter_mut()
.map(|rdr| rdr.byte_records())
.collect::<Vec<_>>();
'OUTER: loop {
let mut record = csv::ByteRecord::new();
let mut num_done = 0;
for (iter, &len) in iters.iter_mut().zip(lengths.iter()) {
match iter.next() {
None => {
num_done += 1;
if self.flag_pad {
for _ in 0..len {
record.push_field(b"");
}
} else {
break 'OUTER;
}
}
Some(Err(err)) => return fail!(err),
Some(Ok(next)) => record.extend(&next),
}
}
if num_done >= iters.len() {
break 'OUTER;
}
wtr.write_byte_record(&record)?;
}
wtr.flush().map_err(From::from)
}
}