use std::path::PathBuf;
use anyhow::bail;
use anyhow::Context;
use clap::arg;
use clap::Args;
use tracing::debug;
use crate::convert::bam;
use crate::convert::cram;
use crate::convert::gff;
use crate::convert::sam;
use crate::utils::args::CompressionStrategy;
use crate::utils::args::NumberOfRecords;
use crate::utils::formats::BioinformaticsFileError;
use crate::utils::formats::BioinformaticsFileFormat;
#[derive(Args)]
pub struct ConvertArgs {
from: PathBuf,
to: PathBuf,
#[arg(short = 'n', long, value_name = "USIZE")]
num_records: Option<usize>,
#[arg(short, long)]
reference_fasta: Option<PathBuf>,
#[arg(short, long, default_value_t = CompressionStrategy::Balanced)]
compression_strategy: CompressionStrategy,
}
pub struct BioinformaticsFilePair(BioinformaticsFileFormat, BioinformaticsFileFormat);
impl BioinformaticsFilePair {
pub fn from(&self) -> &BioinformaticsFileFormat {
&self.0
}
pub fn to(&self) -> &BioinformaticsFileFormat {
&self.1
}
}
pub fn convert(args: ConvertArgs) -> anyhow::Result<()> {
let from = BioinformaticsFileFormat::try_detect(&args.from)
.ok_or(BioinformaticsFileError::FailedParsing)
.with_context(|| format!("from input file: {}", &args.from.display()))?;
let to = BioinformaticsFileFormat::try_detect(&args.to)
.ok_or(BioinformaticsFileError::FailedParsing)
.with_context(|| format!("to input file: {}", args.to.display()))?;
debug!(
"If applicable, using the {} compression strategy.",
args.compression_strategy
);
let max_records = NumberOfRecords::from(args.num_records);
let pair = BioinformaticsFilePair(from, to);
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
match pair {
BioinformaticsFilePair(BioinformaticsFileFormat::SAM, BioinformaticsFileFormat::BAM) => rt
.block_on(sam::to_bam_async(
args.from,
args.to,
max_records,
args.compression_strategy,
)),
BioinformaticsFilePair(BioinformaticsFileFormat::BAM, BioinformaticsFileFormat::SAM) => {
rt.block_on(bam::to_sam_async(args.from, args.to, max_records))
}
BioinformaticsFilePair(BioinformaticsFileFormat::SAM, BioinformaticsFileFormat::CRAM) => {
let fasta = match args.reference_fasta {
Some(s) => s,
None => bail!(
"--reference-fasta is a required argument when converting to/from a CRAM file"
),
};
rt.block_on(sam::to_cram_async(args.from, args.to, fasta, max_records))
}
BioinformaticsFilePair(BioinformaticsFileFormat::CRAM, BioinformaticsFileFormat::SAM) => {
let fasta = match args.reference_fasta {
Some(s) => s,
None => bail!(
"--reference-fasta is a required argument when converting to/from a CRAM file"
),
};
rt.block_on(cram::to_sam_async(args.from, args.to, fasta, max_records))
}
BioinformaticsFilePair(BioinformaticsFileFormat::BAM, BioinformaticsFileFormat::CRAM) => {
let fasta = match args.reference_fasta {
Some(s) => s,
None => bail!(
"--reference-fasta is a required argument when converting to/from a CRAM file"
),
};
rt.block_on(bam::to_cram_async(args.from, args.to, fasta, max_records))
}
BioinformaticsFilePair(BioinformaticsFileFormat::CRAM, BioinformaticsFileFormat::BAM) => {
let fasta = match args.reference_fasta {
Some(s) => s,
None => bail!(
"--reference-fasta is a required argument when converting to/from a CRAM file"
),
};
rt.block_on(cram::to_bam_async(
args.from,
args.to,
fasta,
max_records,
args.compression_strategy,
))
}
BioinformaticsFilePair(
BioinformaticsFileFormat::GFF,
BioinformaticsFileFormat::GFF_BGZ,
) => gff::to_block_gzipped_gff(args.from, args.to, max_records, args.compression_strategy),
_ => bail!(
"Conversion from {} to {} is not currently supported",
pair.from(),
pair.to()
),
}
}