1pub mod error;
17pub mod palettes;
18
19use core::fmt;
20use error::AaColourError;
21use palettes::{Palette, Rgb};
22
23#[derive(Debug, Clone)]
25pub struct AaColour {
26 aa: char,
27 colour: &'static Rgb,
28}
29
30impl fmt::Display for AaColour {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 f.write_fmt(format_args!(
33 "\x1B[48;2;{};{};{};30m",
34 self.colour.r, self.colour.g, self.colour.b
35 ))?;
36 fmt::Display::fmt(&self.aa, f)?;
37 f.write_str("\x1b[0m")
38 }
39}
40
41impl AaColour {
42 pub fn colour<P: Palette>(x: char) -> Result<Self, AaColourError> {
54 let col = match_char_aa::<P>(x)?;
55
56 Ok(Self { aa: x, colour: col })
57 }
58
59 pub fn blank<P: Palette>(x: char) -> Result<Self, AaColourError> {
71 Ok(Self {
72 aa: x,
73 colour: P::GAP,
74 })
75 }
76}
77
78macro_rules! match_aa {
79 ($(
80 $aa:literal $colour:ident
81 ),* $(,)?) => {
82 fn match_char_aa<P: Palette>(x: char) -> Result<&'static Rgb, AaColourError> {
83 match x.to_ascii_uppercase() {
84 $(
85 $aa => Ok(P::$colour),
86 )*
87 _ => Err(AaColourError::NotAnAminoAcid(x)),
88 }
89 }
90 };
91}
92
93match_aa! {
94 'A' A,
95 'C' C,
96 'D' D,
97 'E' E,
98 'F' F,
99 'G' G,
100 'H' H,
101 'I' I,
102 'K' K,
103 'L' L,
104 'M' M,
105 'N' N,
106 'P' P,
107 'Q' Q,
108 'R' R,
109 'S' S,
110 'T' T,
111 'V' V,
112 'W' W,
113 'Y' Y,
114 '-' GAP
115}
116
117#[cfg(test)]
118mod test {
119 use super::*;
120 use crate::palettes::Clustal;
121
122 #[test]
123 fn all_amino_acids_clustal() {
124 let aas = vec![
125 'A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T',
126 'V', 'W', '-',
127 ];
128 for aa in aas {
129 print!("{}", AaColour::colour::<Clustal>(aa).unwrap());
130 }
131 println!(); }
133
134 #[test]
135 fn all_amino_acids_clustal_blanks() {
136 let aas = vec![
137 'A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T',
138 'V', 'W', '-',
139 ];
140 for aa in aas {
141 print!("{}", AaColour::blank::<Clustal>(aa).unwrap());
142 }
143 println!(); }
145}