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;