use noodles::vcf::{self};
use clap::Parser;
use schema::{format_fields_strings, info_fields_strings, main_fields_strings};
use utils::{main_fields_from_record, push_fmts_cartesian, push_fmts_stacked, push_info_fields};
use std::error::Error;
use std::io::{BufReader, Read, Write};
mod cli;
mod consts;
mod schema;
mod specification;
mod utils;
use cli::Opt;
use specification::read_header_lenient;
#[allow(clippy::missing_panics_doc)]
#[allow(clippy::missing_errors_doc)]
pub fn tidyvcf() -> Result<(), Box<dyn Error>> {
let opt = Opt::parse();
let input: Box<dyn Read> = opt.setup_input()?;
let output: Box<dyn Write> = opt.setup_output()?;
convert_records(input, output, opt)?;
Ok(())
}
fn convert_records(
input: Box<dyn Read>,
writer: Box<dyn Write>,
opt: Opt,
) -> Result<(), Box<dyn Error>> {
let mut vcfreader = vcf::io::Reader::new(BufReader::new(input));
let mut writer = csv::WriterBuilder::new()
.delimiter(if opt.csv { b',' } else { b'\t' })
.from_writer(writer);
let headerdata: vcf::Header = if opt.lenient {
read_header_lenient(vcfreader.get_mut())?
} else {
vcfreader.read_header()?
};
let samples = headerdata.sample_names();
let info_keys = headerdata
.infos()
.keys()
.filter(|k| opt.include_info_column(k.as_ref()))
.collect();
let fmt_keys = headerdata
.formats()
.keys()
.filter(|k| opt.include_column(k.as_ref()))
.collect();
let mut hdr_strings = main_fields_strings(&opt);
let number_csq = info_fields_strings(&mut hdr_strings, &headerdata, &opt)?;
format_fields_strings(
&mut hdr_strings,
&headerdata,
&opt,
samples.iter().map(ToString::to_string).collect(),
);
writer.write_record(&hdr_strings)?;
for result in vcfreader.record_bufs(&headerdata) {
let record = result?;
let mut fields = main_fields_from_record(&record, &opt);
push_info_fields(&info_keys, record.info(), &mut fields, number_csq, &opt);
if opt.stack {
let fmt_rows = push_fmts_stacked(&record, &fmt_keys, &opt, samples)?;
for fmt_row in fmt_rows {
writer.write_record(fields.iter().chain(fmt_row.iter()))?;
}
} else {
push_fmts_cartesian(&mut fields, &record, &fmt_keys, &opt)?;
writer.write_record(&fields)?;
}
}
Ok(())
}