use anyhow::{bail, Context, Result};
use num_format::{Locale, ToFormattedString};
use std::{
collections::VecDeque,
fmt,
fs::File,
io::{BufRead, BufReader, Write},
sync::{
atomic::{AtomicBool, AtomicU32, Ordering},
Arc, Mutex,
},
};
use flate2::read::MultiGzDecoder;
use crate::parse::RawSequenceRead;
pub fn read_fastq(
fastq: String,
seq_clone: Arc<Mutex<VecDeque<String>>>,
exit_clone: Arc<AtomicBool>,
total_reads_arc: Arc<AtomicU32>,
) -> Result<()> {
let mut fastq_line_reader = FastqLineReader::new(seq_clone, exit_clone);
let fastq_file = File::open(&fastq).context(format!("Failed to open file: {}", fastq))?; if !fastq.ends_with("fastq.gz") {
if !fastq.ends_with("fastq") {
bail!("This program only works with *.fastq files and *.fastq.gz files. The latter is still experimental")
}
let mut stdout = std::io::stdout();
let mut lock = stdout.lock();
for line_result in BufReader::new(fastq_file).lines() {
let mut line =
line_result.context(format!("Bufread could not read line for file: {}", fastq))?;
line.push('\n');
fastq_line_reader.read(line);
if fastq_line_reader.line_num == 4 {
fastq_line_reader.post()?;
}
if fastq_line_reader.total_reads % 10000 == 0 {
write!(lock, "{}", fastq_line_reader)?;
stdout.flush()?;
}
}
} else {
println!("If this program stops reading before the expected number of sequencing reads, unzip the gzipped fastq and rerun.");
println!();
let mut reader = BufReader::new(MultiGzDecoder::new(fastq_file));
let mut stdout = std::io::stdout();
let mut lock = stdout.lock();
let mut read_response = 10;
while read_response != 0 {
let mut line = String::new();
read_response = reader.read_line(&mut line)?;
fastq_line_reader.read(line);
if fastq_line_reader.line_num == 4 {
fastq_line_reader.post()?;
}
if fastq_line_reader.total_reads % 10000 == 0 {
write!(lock, "{}", fastq_line_reader)?;
stdout.flush()?;
}
}
}
print!("{}", fastq_line_reader);
total_reads_arc.store(fastq_line_reader.total_reads, Ordering::Relaxed);
println!();
Ok(())
}
struct FastqLineReader {
test: bool, line_num: u8, total_reads: u32, raw_sequence_read_string: String,
seq_clone: Arc<Mutex<VecDeque<String>>>, exit_clone: Arc<AtomicBool>, }
impl FastqLineReader {
pub fn new(seq_clone: Arc<Mutex<VecDeque<String>>>, exit_clone: Arc<AtomicBool>) -> Self {
FastqLineReader {
test: true,
line_num: 0,
total_reads: 0,
raw_sequence_read_string: String::new(),
seq_clone,
exit_clone,
}
}
pub fn read(&mut self, line: String) {
while self.seq_clone.lock().unwrap().len() >= 10000 {
if self.exit_clone.load(Ordering::Relaxed) {
break;
}
}
self.line_num += 1;
if self.line_num == 5 {
self.line_num = 1
}
if self.line_num == 1 {
self.total_reads += 1;
self.raw_sequence_read_string = line;
} else {
self.raw_sequence_read_string.push_str(&line);
}
}
pub fn post(&mut self) -> Result<()> {
self.raw_sequence_read_string.pop(); if self.test {
RawSequenceRead::unpack(self.raw_sequence_read_string.clone())?.check_fastq_format()?;
self.test = false;
}
self.seq_clone
.lock()
.unwrap()
.push_front(self.raw_sequence_read_string.clone());
Ok(())
}
}
impl fmt::Display for FastqLineReader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Total sequences: {}\r",
self.total_reads.to_formatted_string(&Locale::en)
)
}
}