aa_name/
lib.rs

1/*! AA-name
2
3Base enum for amino acids (20 basic proteinoformes amino acids)
4
5``` rust
6let ala: AminoAcid = "Ala".parse().unwrap();
7assert_eq!(ala, AminoAcid::Alanine);
8
9let tyr: AminoAcid = AminoAcid::try_from('Y').unwrap();
10assert_eq!(tyr, AminoAcid::Tyrosine);
11```
12
13*/
14
15use std::{convert::TryFrom, str::FromStr};
16use thiserror::Error;
17
18/// # Amino acid codes
19///
20/// As defined in [Dayhoff1965].
21///
22/// ```rust
23/// use aa_name::{AminoAcid, Code};
24///
25/// let alanine = AminoAcid::Alanine;
26/// let tyrosine = AminoAcid::Tyrosine;
27///
28/// // get one letter code
29/// assert_eq!(alanine.single_letter(), 'A');
30///
31/// // get three letters code
32/// assert_eq!(tyrosine.three_letters(), "Tyr");
33///
34/// let mut buffer = Vec::new();
35/// tyrosine.write_single_letter(&mut buffer).unwrap();
36/// alanine.write_single_letter(&mut buffer).unwrap();
37/// assert_eq!(std::str::from_utf8(&buffer).unwrap(), "YA");
38///
39/// let mut buffer = Vec::new();
40/// tyrosine.write_three_letters(&mut buffer).unwrap();
41/// alanine.write_three_letters(&mut buffer).unwrap();
42/// assert_eq!(std::str::from_utf8(&buffer).unwrap(), "TyrAla");
43/// ```
44///
45/// ## References
46/// - [Dayhoff1965]: Dayhoff, MO, RV Eck, MA Chang, and MR Sochard. 1965. ‘Atlas of Protein Sequence and Structure’. <https://ntrs.nasa.gov/citations/19660014530>
47pub trait Code {
48    fn single_letter(&self) -> char;
49    fn three_letters(&self) -> String;
50    fn write_single_letter(
51        &self,
52        writer: impl std::io::Write,
53    ) -> std::result::Result<(), std::io::Error>;
54    fn write_three_letters(
55        &self,
56        writer: impl std::io::Write,
57    ) -> std::result::Result<(), std::io::Error>;
58}
59
60#[derive(Error, Debug)]
61pub enum ErrorKind {
62    #[error("cannot parse amino acid from: `{0}`")]
63    ParseAminoAcidError(String),
64}
65
66macro_rules! aa {
67
68    ($($name:ident $one_letter_code:literal $full_name:literal $three_letters_code:literal),*) => {
69
70        /// One variant for each amino acid
71        ///
72        /// ``` rust
73        /// let ala: AminoAcid = "Ala".parse().unwrap();
74        /// assert_eq!(ala, AminoAcid::Alanine);
75        ///
76        /// let tyr: AminoAcid = AminoAcid::try_from('Y').unwrap();
77        /// assert_eq!(tyr, AminoAcid::Tyrosine);
78        /// ```
79        #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
80        pub enum AminoAcid {
81            $(
82                $name,
83            )*
84        }
85
86        impl ::std::fmt::Display for AminoAcid {
87            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
88                match *self {
89                    $(
90                        Self::$name => write!(f, $full_name)
91                    ),*
92                }
93            }
94        }
95
96        impl Code for AminoAcid {
97            fn single_letter(&self) -> char {
98                match *self {
99                    $(
100                        Self::$name => $one_letter_code
101                    ),*
102                }
103
104            }
105
106            fn three_letters(&self) -> String {
107                match *self {
108                    $(
109                        Self::$name => String::from($three_letters_code),
110                    )*
111                }
112            }
113
114            fn write_single_letter(&self, mut writer: impl std::io::Write) -> std::result::Result<(), std::io::Error> {
115                match *self {
116                    $(
117                        Self::$name => write!(writer, "{}", $one_letter_code)
118                    ),*
119                }
120            }
121
122            fn write_three_letters(&self, mut writer: impl std::io::Write) -> std::result::Result<(), std::io::Error> {
123                match *self {
124                    $(
125                        Self::$name => write!(writer, $three_letters_code)
126                    ),*
127                }
128            }
129        }
130
131        impl FromStr for AminoAcid {
132            type Err = ErrorKind;
133
134            fn from_str(s: &str) -> Result<Self, Self::Err> {
135                match s {
136                    $(
137                        $three_letters_code | $full_name => Ok(Self::$name),
138                    )*
139                    _ => Err(ErrorKind::ParseAminoAcidError(s.to_string())),
140                }
141            }
142        }
143
144        impl TryFrom<char> for AminoAcid {
145            type Error = ErrorKind;
146
147            fn try_from(value: char) -> Result<Self, Self::Error> {
148                match value {
149                    $(
150                        $one_letter_code => Ok(Self::$name),
151                    )*
152                    _ => Err(ErrorKind::ParseAminoAcidError(value.to_string())),
153                }
154            }
155        }
156    };
157}
158
159aa! {
160    Alanine 'A' "Alanine" "Ala",
161    Arginine 'R' "Arginine" "Arg",
162    Asparagine 'N' "Asparagine" "Asn",
163    AsparticAcid 'D' "Aspartic acid" "Asp",
164    Cysteine 'C' "Cysteine" "Cys",
165    GlutamicAcid 'E' "Glutamic acid" "Glu",
166    Glutamine 'Q' "Glutamine" "Gln",
167    Glycine 'G' "Glycine" "Gly",
168    Histidine 'H' "Histidine" "His",
169    Isoleucine 'I' "Isoleucine" "Ile",
170    Leucine 'L' "Leucine" "Leu",
171    Lysine 'K' "Lysine" "Lys",
172    Methionine 'M' "Methionine" "Met",
173    Phenylalanine 'F' "Phenylalanine" "Phe",
174    Proline 'P' "Proline" "Pro",
175    Serine 'S' "Serine" "Ser",
176    Threonine 'T' "Threonine" "Thr",
177    Tryptophan 'W' "Tryptophan" "Trp",
178    Tyrosine 'Y' "Tyrosine" "Tyr",
179    Valine 'V' "Valine" "Val"
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_from_string() {
188        let ala: AminoAcid = "Ala".parse().unwrap();
189
190        assert_eq!(ala, AminoAcid::Alanine);
191    }
192
193    #[test]
194    fn test_from_char() {
195        let tyr: AminoAcid = AminoAcid::try_from('Y').unwrap();
196        assert_eq!(tyr, AminoAcid::Tyrosine)
197    }
198}