use crate::parser::{parse_declared_sections, parse_single_frame};
use crate::{error, types};
use std::iter::Peekable;
use std::path::Path;
pub struct ConFrameIterator<'a> {
lines: Peekable<std::str::Lines<'a>>,
}
impl<'a> ConFrameIterator<'a> {
pub fn new(file_contents: &'a str) -> Self {
ConFrameIterator {
lines: file_contents.lines().peekable(),
}
}
pub fn forward(&mut self) -> Option<Result<(), error::ParseError>> {
self.lines.peek()?;
for _ in 0..6 {
if self.lines.next().is_none() {
return Some(Err(error::ParseError::IncompleteHeader));
}
}
let natm_types: usize = match self.lines.next() {
Some(line) => match crate::parser::parse_line_of_n::<usize>(line, 1) {
Ok(v) => v[0],
Err(e) => return Some(Err(e)),
},
None => return Some(Err(error::ParseError::IncompleteHeader)),
};
let natms_per_type: Vec<usize> = match self.lines.next() {
Some(line) => match crate::parser::parse_line_of_n(line, natm_types) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
},
None => return Some(Err(error::ParseError::IncompleteHeader)),
};
if self.lines.next().is_none() {
return Some(Err(error::ParseError::IncompleteHeader));
}
let total_atoms: usize = natms_per_type.iter().sum();
let non_atom_lines = natm_types * 2;
let lines_to_skip = total_atoms + non_atom_lines;
for _ in 0..lines_to_skip {
if self.lines.next().is_none() {
return Some(Err(error::ParseError::IncompleteFrame));
}
}
loop {
match self.lines.peek() {
Some(line) if line.trim().is_empty() => {
self.lines.next(); let section_lines = total_atoms + non_atom_lines;
for _ in 0..section_lines {
if self.lines.next().is_none() {
return Some(Err(error::ParseError::IncompleteFrame));
}
}
}
_ => break,
}
}
Some(Ok(()))
}
}
impl<'a> Iterator for ConFrameIterator<'a> {
type Item = Result<types::ConFrame, error::ParseError>;
fn next(&mut self) -> Option<Self::Item> {
self.lines.peek()?;
let mut frame = match parse_single_frame(&mut self.lines) {
Ok(f) => f,
Err(e) => return Some(Err(e)),
};
match parse_declared_sections(&mut self.lines, &mut frame.header, &mut frame.atom_data) {
Ok(_) => {}
Err(e) => return Some(Err(e)),
}
Some(Ok(frame))
}
}
pub fn read_all_frames(path: &Path) -> Result<Vec<types::ConFrame>, Box<dyn std::error::Error>> {
let contents = crate::compression::read_file_contents(path)?;
let text = contents.as_str()?;
let iter = ConFrameIterator::new(text);
let frames: Result<Vec<_>, _> = iter.collect();
Ok(frames?)
}
pub fn read_first_frame(path: &Path) -> Result<types::ConFrame, Box<dyn std::error::Error>> {
let contents = crate::compression::read_file_contents(path)?;
let text = contents.as_str()?;
let mut iter = ConFrameIterator::new(text);
match iter.next() {
Some(Ok(frame)) => Ok(frame),
Some(Err(e)) => Err(Box::new(e)),
None => Err("No frames found in file".into()),
}
}
#[cfg(feature = "parallel")]
pub fn parse_frames_parallel(
file_contents: &str,
) -> Vec<Result<types::ConFrame, error::ParseError>> {
use rayon::prelude::*;
let mut boundaries: Vec<usize> = Vec::new();
boundaries.push(0);
let mut scanner = ConFrameIterator::new(file_contents);
while scanner.forward().is_some() {
}
boundaries.clear();
let lines: Vec<&str> = file_contents.lines().collect();
let mut line_idx = 0;
let total_lines = lines.len();
while line_idx < total_lines {
let byte_offset: usize = lines[..line_idx]
.iter()
.map(|l| l.len() + 1) .sum();
boundaries.push(byte_offset);
if line_idx + 6 >= total_lines {
break;
}
line_idx += 6;
let natm_types: usize = match lines.get(line_idx) {
Some(l) => match crate::parser::parse_line_of_n::<usize>(l, 1) {
Ok(v) => v[0],
Err(_) => break,
},
None => break,
};
line_idx += 1;
let natms_per_type: Vec<usize> = match lines.get(line_idx) {
Some(l) => match crate::parser::parse_line_of_n(l, natm_types) {
Ok(v) => v,
Err(_) => break,
},
None => break,
};
line_idx += 1;
line_idx += 1;
let total_atoms: usize = natms_per_type.iter().sum();
let coord_lines = total_atoms + natm_types * 2;
line_idx += coord_lines;
while line_idx < total_lines {
if let Some(l) = lines.get(line_idx) {
if l.trim().is_empty() {
line_idx += 1; line_idx += coord_lines; } else {
break;
}
} else {
break;
}
}
}
let num_frames = boundaries.len();
(0..num_frames)
.into_par_iter()
.map(|i| {
let start = boundaries[i];
let end = if i + 1 < num_frames {
boundaries[i + 1]
} else {
file_contents.len()
};
let chunk = &file_contents[start..end];
let mut iter = ConFrameIterator::new(chunk);
match iter.next() {
Some(result) => result,
None => Err(error::ParseError::IncompleteFrame),
}
})
.collect()
}