pdbtbx/lib.rs
1//! # pdbtbx (PDB Toolbox)
2//!
3//! A library to work with crystallographic Protein DataBank files. It can parse the main part
4//! of the PDB and mmCIF format (it is actively in development so more will follow). The resulting structure
5//! can be used to edit and interrogate the 3D structure of the protein. The changed structures can
6//! be saved in a PDB or mmCIF file for use in other software.
7//!
8//! ## Goals
9//! This library is designed to be a dependable, safe, stable and fast way of handling PDB files
10//! in idiomatic Rust. It is the goal to be very community driven, to make it into a project that
11//! is as useful to everyone while keeping true to its core principles.
12//!
13//! ## Why
14//! As Rust is a recent language so there is not a lot of support for scientific work in Rust
15//! compared to languages that are used much longer (like the ubiquitous Python). I think
16//! that using Rust would have huge benefits over other languages in bigger scientific projects.
17//! It is not just me, more scientists are turning to Rust [`Perkel, J. M.`]. I want to make it
18//! easier for scientists to start using Rust by writing this library.
19//!
20//! ## How to use it
21//! The following example opens a pdb file (`1ubq.pdb`). Removes all `H` atoms. Calculates the
22//! average B factor (or temperature factor) and prints that. It also saves the resulting PDB
23//! to a file.
24//!
25//! ```rust
26//! use pdbtbx::*;
27//! let (mut pdb, _errors) = pdbtbx::open("example-pdbs/1ubq.pdb").unwrap();
28//!
29//! pdb.remove_atoms_by(|atom| atom.element() == Some(&Element::H)); // Remove all H atoms
30//!
31//! let mut avg_b_factor = 0.0;
32//! for atom in pdb.atoms() { // Iterate over all atoms in the structure
33//! avg_b_factor += atom.b_factor();
34//! }
35//! avg_b_factor /= pdb.atom_count() as f64;
36//!
37//! println!("The average B factor of the protein is: {}", avg_b_factor);
38//! pdbtbx::save(&pdb, "dump/1ubq_no_hydrogens.pdb", pdbtbx::StrictnessLevel::Loose);
39//! ```
40//!
41//! ## High level documentation
42//! [general_docs]
43//!
44//! ## Parallelization
45//! [Rayon](https://crates.io/crates/rayon) is used to create parallel iterators for all logical candidates. Use
46//! the parallel version of an iterator by prefixing the name with `par_`. Among other the looping iterators,
47//! like `atoms()`, `residues()` and `atoms_with_hierarchy()` are implemented as parallel iterators. The Rayon
48//! implementations are gated behind the `rayon` [feature](https://doc.rust-lang.org/cargo/reference/features.html)
49//! which is enabled by default.
50//!
51//! ## Serialization
52//! Enable the `serde` feature for [Serde](https://crates.io/crates/serde) support.
53//!
54//! ## Spatial lookup of atoms
55//! Enable the `rstar` feature for [rstar](https://crates.io/crates/rstar) support. This enables you to generate
56//! R*trees making it possible to do very fast lookup for atoms with spatial queries. So for example finding close
57//! atoms is very fast. See the documentation of this crate for more information on how to make use of all of its
58//! features.
59//!
60#![cfg_attr(
61 feature = "rstar",
62 doc = r##"
63```rust
64use pdbtbx::*;
65let (mut pdb, _errors) = pdbtbx::open("example-pdbs/1ubq.pdb").unwrap();
66// You can loop over all atoms within 3.5 Aͦ of a specific atom
67// Note: The `locate_within_distance` method takes a squared distance
68let tree = pdb.create_atom_rtree();
69for atom in tree.locate_within_distance(pdb.atom(42).unwrap().pos(), 3.5 * 3.5) {
70 println!("{}", atom);
71}
72
73// You can even get information about the hierarchy of these atoms
74// (the chain, residue and conformer that contain this atom)
75let tree = pdb.create_hierarchy_rtree();
76let mut total = 0;
77for hierarchy in tree.locate_within_distance(pdb.atom(42).unwrap().pos(), 3.5 * 3.5) {
78 if hierarchy.is_backbone() {
79 total += 1;
80 }
81}
82println!("There are {} backbone atoms within 3.5Aͦ of the atom at index 42", total);
83# assert_eq!(total, 6);
84```
85"##
86)]
87#![doc = "## References"]
88#![doc = "1. [`Perkel, J. M.`] Perkel, J. M. (2020). Why scientists are turning to Rust. Nature, 588(7836), 185–186. [https://doi.org/10.1038/d41586-020-03382-2](https://doi.org/10.1038/d41586-020-03382-2)"]
89// Set linting behaviour
90#![warn(
91 missing_docs,
92 trivial_casts,
93 trivial_numeric_casts,
94 missing_debug_implementations,
95 unused,
96 rust_2018_idioms
97)]
98#![warn(
99 clippy::all,
100 clippy::cast_possible_truncation,
101 clippy::cast_possible_wrap,
102 clippy::cast_precision_loss,
103 clippy::cast_sign_loss,
104 clippy::enum_glob_use,
105 clippy::implicit_clone,
106 clippy::map_unwrap_or,
107 clippy::missing_docs_in_private_items,
108 clippy::nonminimal_bool,
109 clippy::print_stdout,
110 clippy::redundant_clone,
111 clippy::redundant_closure_for_method_calls,
112 clippy::redundant_closure,
113 clippy::similar_names,
114 clippy::single_match_else,
115 clippy::unwrap_used,
116 clippy::use_debug
117)]
118//#![warn(clippy::missing_const_for_fn)]
119//#![warn(clippy::pedantic)] // Activate if in for a nice evening of clippy love 💖
120#![allow(clippy::upper_case_acronyms)] // Allow PDB (and derived) names to be used
121#![cfg_attr(feature = "unstable-doc-cfg", feature(doc_cfg))]
122
123/// To save and display errors
124mod error;
125/// To open PDB files
126mod read;
127/// Reference tables for constants
128mod reference_tables;
129/// To save PDB files
130mod save;
131/// To determine the level of scrutiny that a step should display
132mod strictness_level;
133mod structs;
134/// To handle transformations
135mod transformation;
136/// To validate certain invariants of PDB files
137mod validate;
138
139#[cfg(doc)]
140pub mod general_docs;
141
142pub use error::*;
143pub use read::*;
144pub use save::*;
145pub use strictness_level::StrictnessLevel;
146pub use structs::*;
147pub use transformation::*;
148pub use validate::{validate, validate_pdb};
149
150/// Helper function to check extensions in filenames
151fn check_extension(filename: impl AsRef<str>, extension: impl AsRef<str>) -> bool {
152 filename
153 .as_ref()
154 .rsplit('.')
155 .next()
156 .map(|ext| ext.eq_ignore_ascii_case(extension.as_ref()))
157 == Some(true)
158}