molframe-pdb 0.5.0

Legacy PDB reading and writing for molframe
Documentation
use super::ReadState;
use crate::fixed;
use crate::header::is_metadata_record;
use crate::reader::lines::Line;
use molframe_core::diagnostic::{Code, Diagnostic};
use molframe_core::span::ByteSpan;
use molframe_core::structure::UnitCell;

impl ReadState<'_> {
    pub(super) fn observe_metadata(&mut self, record: &str, line: &Line<'_>) {
        if is_metadata_record(record) {
            // The dense residue column belongs to the first model only.
            if matches!(record, "HELIX" | "SHEET")
                && (self.topology_locked || matches!(self.model_read, super::ModelRead::Ignoring))
            {
                return;
            }
            self.headers.push(record, line.text);
        }
    }

    pub(super) fn cell(&mut self, line: &Line<'_>) {
        let lengths = [
            fixed::real(line.text, 7, 15),
            fixed::real(line.text, 16, 24),
            fixed::real(line.text, 25, 33),
        ];
        let angles = [
            fixed::real(line.text, 34, 40),
            fixed::real(line.text, 41, 47),
            fixed::real(line.text, 48, 54),
        ];
        let (Some(a), Some(b), Some(c)) = (lengths[0], lengths[1], lengths[2]) else {
            self.findings.push(
                Diagnostic::new(Code::E1202)
                    .with_message("cell lengths could not be read")
                    .at(ByteSpan::empty(line.at)),
            );
            return;
        };
        let (Some(alpha), Some(beta), Some(gamma)) = (angles[0], angles[1], angles[2]) else {
            return;
        };
        self.data.cell = Some(UnitCell {
            lengths: [a, b, c],
            angles: [alpha, beta, gamma],
        });
        let symbol = fixed::text(line.text, 56, 66);
        self.data.entry.space_group = (!symbol.is_empty()).then(|| symbol.into());
    }

    pub(super) fn header(&mut self, line: &Line<'_>) {
        let id = fixed::text(line.text, 63, 66);
        if !id.is_empty() {
            self.data.entry.id = Some(id.into());
        }
    }

    pub(super) fn title(&mut self, line: &Line<'_>) {
        append(&mut self.data.entry.title, fixed::text(line.text, 11, 80));
    }

    pub(super) fn method(&mut self, line: &Line<'_>) {
        append(&mut self.data.entry.method, fixed::text(line.text, 11, 79));
    }
}

fn append(target: &mut Option<Box<str>>, text: &str) {
    if text.is_empty() {
        return;
    }
    *target = Some(match target.take() {
        Some(existing) => format!("{existing} {text}").into(),
        None => text.into(),
    });
}