use super::numbers::AtomNumbers;
use crate::fixed;
use molframe_core::diagnostic::{Code, Diagnostic};
use molframe_core::span::Position;
#[derive(Clone, Copy)]
pub(super) struct Line<'a> {
pub(super) text: &'a str,
pub(super) at: Position,
pub(super) numbers: Option<&'a AtomNumbers>,
}
impl Line<'_> {
pub(super) fn occupancy(&self) -> Option<f64> {
match self.numbers {
Some(numbers) => numbers.occupancy,
None => fixed::real(self.text, 55, 60),
}
}
pub(super) fn b_factor(&self) -> Option<f64> {
match self.numbers {
Some(numbers) => numbers.b_factor,
None => fixed::real(self.text, 61, 66),
}
}
}
pub(super) struct Lines<'a> {
remaining: &'a str,
at: Position,
}
impl<'a> Lines<'a> {
pub(super) fn new(text: &'a str) -> Self {
Self {
remaining: text,
at: Position::START,
}
}
}
impl<'a> Iterator for Lines<'a> {
type Item = Result<Line<'a>, Diagnostic>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining.is_empty() {
return None;
}
let at = self.at;
let (line, rest) = match self.remaining.find('\n') {
Some(end) => (self.remaining.get(..end)?, self.remaining.get(end + 1..)?),
None => (self.remaining, ""),
};
self.remaining = rest;
let advance = u64::try_from(line.len())
.ok()
.and_then(|length| length.checked_add(1));
let Some((byte_offset, line_number)) = advance
.and_then(|advance| at.byte_offset.checked_add(advance))
.zip(at.line.checked_add(1))
else {
self.remaining = "";
return Some(Err(Diagnostic::new(Code::E1202)
.with_message("PDB source position exceeds the supported range")));
};
self.at = Position::new(byte_offset, line_number, 1);
Some(Ok(Line {
text: line.trim_end_matches('\r'),
at,
numbers: None,
}))
}
}