use rayon::prelude::*;
use seq_io::fastq::{OwnedRecord, RefRecord};
use crate::fastq::{CompressionType, FastqReader, FastqWriter, FastqsReader, FastqsWriter};
use crate::logger;
use crate::parse::{self, BarcodeParser};
use crate::pattern::BarcodeRegex;
#[allow(clippy::too_many_arguments)]
pub fn run(
fq1: String,
fq2: Option<String>,
pattern1: Option<String>,
pattern2: Option<String>,
out_fq1: String,
out_fq2: Option<String>,
max_memory: Option<usize>,
threads: usize,
rc_barcodes: bool,
skip_trimming: bool,
max_error: usize,
output_compression: CompressionType,
quiet: bool,
force: bool,
) {
match (fq2, out_fq2, pattern1, pattern2) {
(Some(fq2), Some(out_fq2), pattern1, pattern2) => process_pair_end_fastq(
fq1,
fq2,
pattern1,
pattern2,
out_fq1,
out_fq2,
max_memory,
threads,
rc_barcodes,
skip_trimming,
max_error,
output_compression,
quiet,
force
),
(None, None, Some(pattern1), None) => process_single_end_fastq(
fq1,
pattern1,
out_fq1,
max_memory,
threads,
rc_barcodes,
skip_trimming,
max_error,
output_compression,
quiet,
force
),
_ => eprintln!(
"Invalid arguments provided. Please ensure that you have provided the correct combination of input files and patterns."
),
}
}
fn parse_se_reads(
records: &Vec<RefRecord>,
barcode: &BarcodeRegex,
skip_trimming: bool,
rc_barcodes: bool,
) -> Vec<OwnedRecord> {
records
.par_iter()
.filter_map(|record| {
let barcodes_parser = BarcodeParser::new(Some(barcode), skip_trimming, rc_barcodes);
barcodes_parser?.parse_barcodes(record)
})
.collect()
}
#[allow(clippy::too_many_arguments)]
fn process_single_end_fastq(
fq: String,
pattern: String,
out_fq: String,
max_memory: Option<usize>,
threads: usize,
rc_barcodes: bool,
skip_trimming: bool,
max_error: usize,
output_compression: CompressionType,
quiet: bool,
force: bool,
) {
let mut logger = logger::Logger::new(3, quiet);
logger.message("Estimating reads count...");
let lines_number = FastqReader::count_reads(&fq, threads, max_memory);
logger.set_progress_bar(lines_number);
let mut reader = FastqReader::new(&fq, threads, max_memory).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
let mut writer =
FastqWriter::new(&out_fq, &output_compression, threads, force).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
logger.message("Parsing barcode patterns...");
let barcode = BarcodeRegex::new(&pattern, max_error).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
logger.message("Extracting barcodes from reads...");
loop {
let record_set = reader.read_record_set();
if let Some(records) = record_set {
let records = records.into_iter().collect::<Vec<_>>();
let result_reads = parse_se_reads(&records, &barcode, skip_trimming, rc_barcodes);
writer.write_all(result_reads).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
logger.increment_progress(records.len());
} else {
break;
}
}
logger.final_message();
}
fn get_new_reads(
new_records: (Option<OwnedRecord>, Option<OwnedRecord>),
record1: &RefRecord,
record2: &RefRecord,
) -> Option<(OwnedRecord, OwnedRecord)> {
match new_records {
(Some(new_record1), Some(new_record2)) => Some((new_record1, new_record2)),
(None, Some(new_record2)) => Some((record1.to_owned_record(), new_record2)),
(Some(new_record1), None) => Some((new_record1, record2.to_owned_record())),
(None, None) => None,
}
}
fn parse_pe_reads(
records1: &Vec<RefRecord>,
records2: &Vec<RefRecord>,
barcode1: &Option<BarcodeRegex>,
barcode2: &Option<BarcodeRegex>,
skip_trimming: bool,
rc_barcodes: bool,
) -> Vec<(OwnedRecord, OwnedRecord)> {
records1
.par_iter()
.zip(records2.par_iter())
.filter_map(|(record1, record2)| {
let barcode1_parser =
parse::BarcodeParser::new(barcode1.as_ref(), skip_trimming, rc_barcodes);
let barcode2_parser =
parse::BarcodeParser::new(barcode2.as_ref(), skip_trimming, rc_barcodes);
let new_reads = (
barcode1_parser
.as_ref()
.and_then(|parser| parser.parse_barcodes(record1)),
barcode2_parser
.as_ref()
.and_then(|parser| parser.parse_barcodes(record2)),
);
get_new_reads(new_reads, record1, record2)
})
.collect()
}
#[allow(clippy::too_many_arguments)]
fn process_pair_end_fastq(
fq1: String,
fq2: String,
pattern1: Option<String>,
pattern2: Option<String>,
out_fq1: String,
out_fq2: String,
max_memory: Option<usize>,
threads: usize,
rc_barcodes: bool,
skip_trimming: bool,
max_error: usize,
output_compression: CompressionType,
quiet: bool,
force: bool,
) {
let mut logger = logger::Logger::new(3, quiet);
logger.message("Estimating reads count...");
let lines_number = FastqReader::count_reads(&fq1, threads, max_memory);
logger.set_progress_bar(lines_number);
let mut reader = FastqsReader::new(&fq1, &fq2, threads, max_memory).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
let mut writer = FastqsWriter::new(&out_fq1, &out_fq2, &output_compression, threads, force)
.unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
logger.message("Parsing barcode patterns...");
let barcode1 = pattern1.as_ref().map(|pat| {
BarcodeRegex::new(pat, max_error).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
})
});
let barcode2 = pattern2.as_ref().map(|pat| {
BarcodeRegex::new(pat, max_error).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
})
});
logger.message("Extracting barcodes from reads...");
loop {
let record_sets = reader.read_record_sets();
if let Ok((Some(records1), Some(records2))) = record_sets {
let records1 = records1.into_iter().collect::<Vec<_>>();
let records2 = records2.into_iter().collect::<Vec<_>>();
let new_reads = parse_pe_reads(
&records1,
&records2,
&barcode1,
&barcode2,
skip_trimming,
rc_barcodes,
);
writer.write_all(new_reads).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
logger.increment_progress(records1.len());
} else {
break;
}
}
logger.final_message();
}