use molframe_core::Structure;
pub const PDB_HEADERS_EXTENSION: &str = "molframe.pdb.headers.v1";
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PdbHeaderRecord {
name: Box<str>,
line: Box<str>,
}
impl PdbHeaderRecord {
pub(crate) fn new(name: &str, line: &str) -> Self {
Self {
name: name.into(),
line: line.into(),
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn line(&self) -> &str {
&self.line
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct PdbHeaders {
records: Vec<PdbHeaderRecord>,
}
impl PdbHeaders {
pub(crate) fn push(&mut self, name: &str, line: &str) {
self.records.push(PdbHeaderRecord::new(name, line));
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.records.is_empty()
}
#[must_use]
pub fn records(&self) -> &[PdbHeaderRecord] {
&self.records
}
pub fn named<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a PdbHeaderRecord> {
self.records
.iter()
.filter(move |record| record.name.as_ref() == name)
}
#[must_use]
pub fn classification(&self) -> Option<&str> {
self.header_field(10, 50)
}
#[must_use]
pub fn deposition_date(&self) -> Option<&str> {
self.header_field(50, 59)
}
fn header_field(&self, start: usize, end: usize) -> Option<&str> {
let header = self.named("HEADER").next()?.line();
let value = header.get(start..end)?.trim();
(!value.is_empty()).then_some(value)
}
}
pub trait PdbHeadersExt {
fn pdb_headers(&self) -> Option<&PdbHeaders>;
}
impl PdbHeadersExt for Structure {
fn pdb_headers(&self) -> Option<&PdbHeaders> {
self.extensions().get(PDB_HEADERS_EXTENSION)
}
}
pub(crate) fn is_metadata_record(name: &str) -> bool {
matches!(
name,
"HEADER"
| "OBSLTE"
| "TITLE"
| "SPLIT"
| "CAVEAT"
| "COMPND"
| "SOURCE"
| "KEYWDS"
| "EXPDTA"
| "NUMMDL"
| "MDLTYP"
| "AUTHOR"
| "REVDAT"
| "SPRSDE"
| "JRNL"
| "REMARK"
| "DBREF"
| "DBREF1"
| "DBREF2"
| "SEQADV"
| "SEQRES"
| "MODRES"
| "HET"
| "HETNAM"
| "HETSYN"
| "FORMUL"
| "HELIX"
| "SHEET"
| "SSBOND"
| "LINK"
| "CISPEP"
| "SITE"
| "CRYST1"
| "ORIGX1"
| "ORIGX2"
| "ORIGX3"
| "SCALE1"
| "SCALE2"
| "SCALE3"
| "MTRIX1"
| "MTRIX2"
| "MTRIX3"
| "MASTER"
)
}