Skip to main content

molframe_pdb/fixed/
fields.rs

1//! Fixed-width fields.
2//!
3//! Columns are 1-based and inclusive, as the format's own documentation quotes
4//! them, and a line shorter than the field is read as though it were padded with
5//! spaces — which is what the Fortran that defined the format did, and what
6//! files written by that Fortran assume.
7//!
8//! Nothing here allocates. A field is a borrowed slice of the line.
9
10/// The text of columns `from` to `to`, trimmed, or the empty string when the
11/// line stops short.
12#[must_use]
13pub fn text(line: &str, from: usize, to: usize) -> &str {
14    raw(line, from, to).trim()
15}
16
17/// The text of columns `from` to `to` without trimming.
18///
19/// The leading space of an atom name is the only thing distinguishing a
20/// one-letter element from a two-letter one, so some fields must not be trimmed
21/// before they are interpreted.
22#[must_use]
23pub fn raw(line: &str, from: usize, to: usize) -> &str {
24    let Some(start) = from.checked_sub(1) else {
25        return "";
26    };
27    let end = to.min(line.len());
28    if start >= end {
29        return "";
30    }
31    let Some(field) = line.get(start..end) else {
32        return "";
33    };
34    field
35}
36
37/// The record name a line carries.
38#[must_use]
39pub fn record(line: &str) -> &str {
40    text(line, 1, 6)
41}
42
43/// An integer read from columns `from` to `to`.
44#[must_use]
45pub fn integer(line: &str, from: usize, to: usize) -> Option<i64> {
46    let field = text(line, from, to);
47    if field.is_empty() {
48        None
49    } else {
50        field.parse().ok()
51    }
52}
53
54/// A real number read from columns `from` to `to`.
55#[must_use]
56pub fn real(line: &str, from: usize, to: usize) -> Option<f64> {
57    let field = text(line, from, to);
58    if field.is_empty() {
59        None
60    } else {
61        field.parse().ok()
62    }
63}
64
65#[cfg(test)]
66#[path = "fields_tests.rs"]
67mod tests;