molframe-cif 0.4.0

PDBx/mmCIF reading and writing for molframe
Documentation
//! Reading one coordinate row's fields, and interning what they name.
//!
//! Separated from the lowering itself so that the row loop reads as the
//! sequence of decisions it makes, rather than as those decisions interleaved
//! with the mechanics of getting a value out of a row.

use super::{AtomBuilder, AtomSiteRow, Field};
use crate::lower::diagnostics::at_source_row;
use molframe_core::column::Presence;
use molframe_core::diagnostic::{Code, Diagnostic};
use molframe_core::element::Element;
use molframe_core::io::MissingElementPolicy;
use molframe_core::optional::OptionalSymbol;
use molframe_core::symbol::{AltId, SymbolId};
use num_traits::ToPrimitive;

impl AtomBuilder<'_> {
    pub(super) fn position_of(&mut self, rows: &dyn AtomSiteRow) -> Option<[f32; 3]> {
        let (Some(x), Some(y), Some(z)) = (
            rows.float(Field::CartnX),
            rows.float(Field::CartnY),
            rows.float(Field::CartnZ),
        ) else {
            self.findings.push(at_source_row(
                Diagnostic::new(Code::E1202)
                    .with_message("a coordinate could not be read")
                    .in_category("atom_site"),
                rows.row(),
            ));
            return None;
        };
        let Some(position) = x
            .to_f32()
            .zip(y.to_f32())
            .zip(z.to_f32())
            .map(|((x, y), z)| [x, y, z])
        else {
            self.findings.push(at_source_row(
                Diagnostic::new(Code::E1202)
                    .with_message("a coordinate is outside the supported floating-point range")
                    .in_category("atom_site"),
                rows.row(),
            ));
            return None;
        };
        Some(position)
    }

    pub(super) fn optional_float(
        &mut self,
        rows: &dyn AtomSiteRow,
        field: Field,
        when_absent: f32,
    ) -> (f32, Presence) {
        let Some(value) = rows.float(field) else {
            return (when_absent, Presence::Unknown);
        };
        if let Some(value) = value.to_f32() {
            return (value, Presence::Present);
        }
        self.findings.push(at_source_row(
            Diagnostic::new(Code::E1202)
                .with_message("an atom value is outside the supported floating-point range")
                .in_category("atom_site")
                .with_context("item", field.item()),
            rows.row(),
        ));
        (when_absent, Presence::Unknown)
    }

    pub(super) fn element_of(&mut self, rows: &dyn AtomSiteRow, name: Option<&str>) -> Element {
        if let Some(element) = rows.text(Field::TypeSymbol).and_then(Element::from_symbol) {
            return element;
        }
        let inferred = match (self.options.missing_element_policy, name) {
            (MissingElementPolicy::InferFromAtomName, Some(name)) => Element::infer_from_name(name),
            _ => Element::UNKNOWN,
        };
        self.findings.push(at_source_row(
            Diagnostic::new(Code::W3203)
                .in_category("atom_site")
                .with_context("inferred", inferred.symbol()),
            rows.row(),
        ));
        inferred
    }

    pub(super) fn auth_name_of(&mut self, rows: &dyn AtomSiteRow) -> OptionalSymbol {
        rows.identifier(Field::AuthAtomId)
            .map_or(OptionalSymbol::NONE, |text| {
                OptionalSymbol::some(self.intern(&text))
            })
    }

    pub(super) fn alt_of(&mut self, rows: &dyn AtomSiteRow) -> Option<AltId> {
        if let Some(text) = rows.identifier(Field::LabelAltId)
            && !text.is_empty()
        {
            return AltId::labelled(self.intern(&text));
        }
        Some(AltId::BLANK)
    }

    pub(super) fn intern(&mut self, text: &str) -> SymbolId {
        let Ok(symbol) = self.data.dictionary.intern(text) else {
            self.findings
                .push(Diagnostic::new(Code::E1901).with_message("the dictionary is full"));
            return SymbolId::from_raw(0);
        };
        symbol
    }
}