use csv;
use crate::config::{Config, Delimiter};
use crate::select::SelectColumns;
use crate::util;
use crate::CliResult;
static USAGE: &str = "
Fill empty cells of a CSV file by filling them with any non-empty value seen
before (this is usually called forward filling), or with any constant value
given to the -v, --value flag.
For instance, replacing empty values with 0 everywhere in the file:
$ xan fill -v 0 data.csv > filled.csv
Usage:
xan fill [options] [<input>]
xan fill --help
fill options:
-s, --select <cols> Selection of columns to fill.
-v, --value <value> Fill empty cells using provided value instead of using
last non-empty value.
Common options:
-h, --help Display this message
-o, --output <file> Write output to <file> instead of stdout.
-n, --no-headers When set, the file will be considered as having no
headers.
-d, --delimiter <arg> The field delimiter for reading CSV data.
Must be a single character.
";
#[derive(Deserialize)]
struct Args {
arg_input: Option<String>,
flag_select: SelectColumns,
flag_value: Option<String>,
flag_no_headers: bool,
flag_delimiter: Option<Delimiter>,
flag_output: Option<String>,
}
pub fn run(argv: &[&str]) -> CliResult<()> {
let args: Args = util::get_args(USAGE, argv)?;
let rconf = Config::new(&args.arg_input)
.delimiter(args.flag_delimiter)
.no_headers(args.flag_no_headers)
.select(args.flag_select);
let mut rdr = rconf.reader()?;
let mut wtr = Config::new(&args.flag_output).writer()?;
let headers = rdr.byte_headers()?;
let sel = rconf.selection(headers)?;
let mask = sel.indexed_mask(headers.len());
rconf.write_headers(&mut rdr, &mut wtr)?;
let mut previous: Option<csv::ByteRecord> = None;
for result in rdr.byte_records() {
let record = result?;
match previous.as_mut() {
None => {
wtr.write_byte_record(&record)?;
previous = Some(record);
}
Some(previous_record) => {
let filled_record = mask
.iter()
.enumerate()
.map(|(i, opt)| {
let current_cell = &record[i];
if opt.is_some() {
if !current_cell.is_empty() {
current_cell
} else if let Some(value) = &args.flag_value {
value.as_bytes()
} else {
&previous_record[i]
}
} else {
current_cell
}
})
.collect::<csv::ByteRecord>();
wtr.write_byte_record(&filled_record)?;
*previous_record = filled_record;
}
};
}
Ok(wtr.flush()?)
}