molframe_core/secondary.rs
1//! Per-residue secondary-structure vocabulary shared by readers and analyses.
2
3/// A residue-level structural assignment, in the DSSP vocabulary.
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
5pub enum SecondaryStructure {
6 /// The file and available analyses supplied no assignment.
7 #[default]
8 Unknown,
9 /// A residue outside every helix, ladder, turn and bend (DSSP blank).
10 Coil,
11 /// An α-helix, i → i+4 (DSSP `H`).
12 AlphaHelix,
13 /// A 3₁₀-helix, i → i+3 (DSSP `G`).
14 ThreeTenHelix,
15 /// A π-helix, i → i+5 (DSSP `I`).
16 PiHelix,
17 /// A helix whose class the source does not name as α, 3₁₀ or π.
18 OtherHelix,
19 /// An isolated β-bridge, a single bridge pair outside a ladder (DSSP `B`).
20 BetaBridge,
21 /// A residue in a β-ladder of two or more bridges (DSSP `E`).
22 Strand,
23 /// A hydrogen-bonded turn (DSSP `T`).
24 Turn,
25 /// A bend: Cα direction changes by more than 70° (DSSP `S`).
26 Bend,
27 /// A left-handed polyproline-II helix (DSSP 4 `P`); not sequence restricted.
28 PolyProline,
29}
30
31impl SecondaryStructure {
32 /// Whether the state is any kind of helix.
33 #[must_use]
34 pub const fn is_helix(self) -> bool {
35 matches!(
36 self,
37 Self::AlphaHelix
38 | Self::ThreeTenHelix
39 | Self::PiHelix
40 | Self::OtherHelix
41 | Self::PolyProline
42 )
43 }
44 /// Whether the residue belongs to a beta ladder rather than an isolated bridge.
45 #[must_use]
46 pub const fn is_strand(self) -> bool {
47 matches!(self, Self::Strand)
48 }
49
50 /// Whether the residue is a beta ladder or an isolated beta bridge.
51 #[must_use]
52 pub const fn is_sheet_like(self) -> bool {
53 matches!(self, Self::Strand | Self::BetaBridge)
54 }
55
56 /// The stable one-byte code exchanged with consumers such as the Python
57 /// binding and viewers. Codes 0–4 predate the 10-state vocabulary and keep
58 /// their meaning; the later states are appended.
59 #[must_use]
60 pub const fn code(self) -> u8 {
61 match self {
62 Self::Unknown => 0,
63 Self::Coil => 1,
64 Self::AlphaHelix => 2,
65 Self::Strand => 3,
66 Self::Turn => 4,
67 Self::ThreeTenHelix => 5,
68 Self::PiHelix => 6,
69 Self::OtherHelix => 7,
70 Self::BetaBridge => 8,
71 Self::Bend => 9,
72 Self::PolyProline => 10,
73 }
74 }
75}
76
77/// Where a residue's secondary-structure state came from.
78#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
79pub enum SecondarySource {
80 /// Nothing assigned the state.
81 #[default]
82 None,
83 /// The deposited file's helix and sheet records.
84 File,
85 /// Kabsch–Sander assignment from backbone hydrogen bonds.
86 Dssp,
87 /// The Cα-only fallback for a trace without backbone atoms.
88 CaOnly,
89}
90
91impl SecondarySource {
92 /// Precedence when two sources disagree; the higher rank wins.
93 #[must_use]
94 pub const fn rank(self) -> u8 {
95 match self {
96 Self::None => 0,
97 Self::CaOnly => 1,
98 Self::Dssp => 2,
99 Self::File => 3,
100 }
101 }
102
103 /// The stable one-byte code exchanged with consumers.
104 #[must_use]
105 pub const fn code(self) -> u8 {
106 match self {
107 Self::None => 0,
108 Self::File => 1,
109 Self::Dssp => 2,
110 Self::CaOnly => 3,
111 }
112 }
113}
114
115/// One residue's state together with where it came from.
116#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
117pub struct SecondaryAssignment {
118 /// The assigned state.
119 pub state: SecondaryStructure,
120 /// The source that assigned it.
121 pub source: SecondarySource,
122}
123
124#[cfg(test)]
125#[path = "secondary_tests.rs"]
126mod tests;