1use strum::EnumIter;
7
8pub const ALPHABET: [char; 21] = [
10 'A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W',
11 'Y', 'X',
12];
13
14#[repr(u8)]
17#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter)]
18pub enum AminoAcid {
19 Ala = 0,
20 Cys = 1,
21 Asp = 2,
22 Glu = 3,
23 Phe = 4,
24 Gly = 5,
25 His = 6,
26 Ile = 7,
27 Lys = 8,
28 Leu = 9,
29 Met = 10,
30 Asn = 11,
31 Pro = 12,
32 Gln = 13,
33 Arg = 14,
34 Ser = 15,
35 Thr = 16,
36 Val = 17,
37 Trp = 18,
38 Tyr = 19,
39 Unknown = 20,
40}
41
42impl AminoAcid {
43 #[rustfmt::skip]
45 pub fn from_three_letter(aa: &str) -> Self {
46 match aa {
47 "ALA" => Self::Ala, "CYS" => Self::Cys, "ASP" => Self::Asp,
48 "GLU" => Self::Glu, "PHE" => Self::Phe, "GLY" => Self::Gly,
49 "HIS" => Self::His, "ILE" => Self::Ile, "LYS" => Self::Lys,
50 "LEU" => Self::Leu, "MET" => Self::Met, "ASN" => Self::Asn,
51 "PRO" => Self::Pro, "GLN" => Self::Gln, "ARG" => Self::Arg,
52 "SER" => Self::Ser, "THR" => Self::Thr, "VAL" => Self::Val,
53 "TRP" => Self::Trp, "TYR" => Self::Tyr, _ => Self::Unknown,
54 }
55 }
56
57 #[rustfmt::skip]
59 pub fn from_one_letter(aa: char) -> Self {
60 match aa {
61 'A' => Self::Ala, 'C' => Self::Cys, 'D' => Self::Asp,
62 'E' => Self::Glu, 'F' => Self::Phe, 'G' => Self::Gly,
63 'H' => Self::His, 'I' => Self::Ile, 'K' => Self::Lys,
64 'L' => Self::Leu, 'M' => Self::Met, 'N' => Self::Asn,
65 'P' => Self::Pro, 'Q' => Self::Gln, 'R' => Self::Arg,
66 'S' => Self::Ser, 'T' => Self::Thr, 'V' => Self::Val,
67 'W' => Self::Trp, 'Y' => Self::Tyr, _ => Self::Unknown,
68 }
69 }
70
71 pub fn from_index(idx: u32) -> Self {
73 match idx {
74 0 => Self::Ala,
75 1 => Self::Cys,
76 2 => Self::Asp,
77 3 => Self::Glu,
78 4 => Self::Phe,
79 5 => Self::Gly,
80 6 => Self::His,
81 7 => Self::Ile,
82 8 => Self::Lys,
83 9 => Self::Leu,
84 10 => Self::Met,
85 11 => Self::Asn,
86 12 => Self::Pro,
87 13 => Self::Gln,
88 14 => Self::Arg,
89 15 => Self::Ser,
90 16 => Self::Thr,
91 17 => Self::Val,
92 18 => Self::Trp,
93 19 => Self::Tyr,
94 _ => Self::Unknown,
95 }
96 }
97
98 #[rustfmt::skip]
100 pub fn one_letter(self) -> char {
101 match self {
102 Self::Ala => 'A', Self::Cys => 'C', Self::Asp => 'D',
103 Self::Glu => 'E', Self::Phe => 'F', Self::Gly => 'G',
104 Self::His => 'H', Self::Ile => 'I', Self::Lys => 'K',
105 Self::Leu => 'L', Self::Met => 'M', Self::Asn => 'N',
106 Self::Pro => 'P', Self::Gln => 'Q', Self::Arg => 'R',
107 Self::Ser => 'S', Self::Thr => 'T', Self::Val => 'V',
108 Self::Trp => 'W', Self::Tyr => 'Y', Self::Unknown => 'X',
109 }
110 }
111
112 #[rustfmt::skip]
114 pub fn three_letter(self) -> &'static str {
115 match self {
116 Self::Ala => "ALA", Self::Cys => "CYS", Self::Asp => "ASP",
117 Self::Glu => "GLU", Self::Phe => "PHE", Self::Gly => "GLY",
118 Self::His => "HIS", Self::Ile => "ILE", Self::Lys => "LYS",
119 Self::Leu => "LEU", Self::Met => "MET", Self::Asn => "ASN",
120 Self::Pro => "PRO", Self::Gln => "GLN", Self::Arg => "ARG",
121 Self::Ser => "SER", Self::Thr => "THR", Self::Val => "VAL",
122 Self::Trp => "TRP", Self::Tyr => "TYR", Self::Unknown => "UNK",
123 }
124 }
125
126 pub fn index(self) -> usize {
128 self as usize
129 }
130}
131
132pub fn aa3to1(aa: &str) -> char {
136 AminoAcid::from_three_letter(aa).one_letter()
137}
138
139pub fn aa1to_int(aa: char) -> u32 {
141 AminoAcid::from_one_letter(aa).index() as u32
142}
143
144pub fn int_to_aa1(aa_int: u32) -> char {
146 AminoAcid::from_index(aa_int).one_letter()
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_aa3to1_roundtrip() {
155 assert_eq!(aa3to1("ALA"), 'A');
156 assert_eq!(aa3to1("TRP"), 'W');
157 assert_eq!(aa3to1("UNK"), 'X');
158 assert_eq!(aa3to1("???"), 'X');
159 }
160
161 #[test]
162 fn test_aa1to_int_roundtrip() {
163 for (i, &ch) in ALPHABET.iter().enumerate() {
164 assert_eq!(aa1to_int(ch) as usize, i, "ALPHABET[{i}] = {ch}");
165 }
166 assert_eq!(aa1to_int('?'), 20);
167 }
168
169 #[test]
170 fn test_int_to_aa1_roundtrip() {
171 for i in 0u32..=20 {
172 let ch = int_to_aa1(i);
173 assert_eq!(aa1to_int(ch), i, "round-trip failed at index {i}");
174 }
175 assert_eq!(int_to_aa1(99), 'X');
176 }
177
178 #[test]
179 fn test_amino_acid_enum_roundtrip() {
180 use strum::IntoEnumIterator;
181 for aa in AminoAcid::iter() {
182 let idx = aa.index() as u32;
183 assert_eq!(AminoAcid::from_index(idx), aa);
184 assert_eq!(AminoAcid::from_one_letter(aa.one_letter()), aa);
185 if aa != AminoAcid::Unknown {
186 assert_eq!(AminoAcid::from_three_letter(aa.three_letter()), aa);
187 }
188 }
189 }
190}