nucleic_acids/
lib.rs

1#![feature(iter_array_chunks)]
2#![feature(associated_type_defaults)]
3
4use std::{marker::PhantomData};
5
6/// Each one of our Nucleotides (T and U are the same).
7#[derive(Debug, Clone, PartialEq)]
8pub enum Nucleotide {
9  Adenine,
10  Cytosine,
11  Guanine,
12  Thymine,
13  Uracil,
14}
15
16/// Codons so we can make proteins out of them (soon).
17#[derive(Debug, PartialEq)]
18pub struct Codon([Nucleotide; 3]);
19
20/// Main NucleicAcid Struct.
21#[derive(Debug)]
22pub struct NucleicAcid<T> {
23    // maybe i can cow this?
24    /// A vector of Nucleotides.
25    pub nucleotides: Vec<Nucleotide>,
26    /// Marker to differentiate between Dna and Rna
27    pub _marker: PhantomData<T>,
28}
29
30/// Dna (used as a marker).
31#[derive(Debug)]
32pub struct Dna;
33/// Rna (used as a marker).
34#[derive(Debug)]
35pub struct Rna;
36
37pub type NucleicRna = NucleicAcid<Rna>;
38pub type NucleicDna = NucleicAcid<Dna>;
39
40/// Builds an Acid (with Self::Marker) from Content
41pub trait NucleicBuild {
42    type Marker;
43    type Acid = NucleicAcid<Self::Marker>;
44
45    fn build(content: &str) -> Result<Self::Acid, String>;
46}
47
48/// Converts a char to a Nucleotide thats Suitable for the Marker.
49pub trait NucleicConvert {
50    fn char_to_nucleotide(c: char) -> Result<Nucleotide, String>;
51}
52
53/// Blanket implementation (if you have NucleicConvert you can use NucleicBuild).
54impl<M> NucleicBuild for NucleicAcid<M>
55where
56    NucleicAcid<M>: NucleicConvert,
57{
58    type Marker = M;
59
60    fn build(content: &str) -> Result<Self::Acid, String> {
61        let nucleotides: Result<Vec<_>, _> = content.chars()
62            .map(|c| Self::Acid::char_to_nucleotide(c))
63            .collect();
64
65        let nucleotides = nucleotides?;
66
67        Ok(Self::Acid { nucleotides, _marker: PhantomData::<Self::Marker>})
68    }
69}
70
71impl NucleicConvert for NucleicRna {
72    /// Impl for NucleicRna.
73    fn char_to_nucleotide(c: char) -> Result<Nucleotide, String> {
74        use Nucleotide::*;
75        
76        Ok(match c.to_ascii_uppercase() {
77            'A' => Adenine,
78            'C' => Cytosine,
79            'G' => Guanine,
80            'U' => Uracil,
81            x => Err(format!("{x}: not a valid character"))?,
82        })
83    } 
84}
85
86impl NucleicRna {
87    /// Transforms a NucleicRna into invidual Codons.
88    pub fn into_codons(self) -> Vec<Codon> {
89        self.nucleotides.into_iter()
90            .array_chunks::<3>()
91            .map(|x| Codon(x))
92            .collect()
93    }
94}
95
96
97
98impl NucleicConvert for NucleicDna {
99    // Impl for NucleicDna.
100    fn char_to_nucleotide(c: char) -> Result<Nucleotide, String> {
101        use Nucleotide::*;
102        
103        Ok(match c.to_ascii_uppercase() {
104            'A' => Adenine,
105            'C' => Cytosine,
106            'G' => Guanine,
107            'T' => Thymine,
108            x => Err(format!("{x}: not a valid character"))?,
109        })
110    }
111}
112
113impl From<NucleicDna> for NucleicRna {
114    fn from(mut value: NucleicDna) -> Self {
115        for x in value.nucleotides.iter_mut() {
116            if *x == Nucleotide::Thymine {
117                *x = Nucleotide::Uracil;
118            }
119        }
120        
121        Self { nucleotides: value.nucleotides, _marker: PhantomData::<Rna>}
122    }
123}
124
125impl From<NucleicRna> for NucleicDna {
126    fn from(mut value: NucleicRna) -> Self {
127        for x in value.nucleotides.iter_mut() {
128            if *x == Nucleotide::Uracil {
129                *x = Nucleotide::Thymine;
130            }
131        }
132        
133        Self { nucleotides: value.nucleotides, _marker: PhantomData::<Dna>}
134    }
135}
136
137#[cfg(test)]
138mod test {
139    use super::*;
140
141    #[test]
142    fn dna_example() {
143        let content = "tAgCaT";
144        let result = NucleicDna::build(content).unwrap();
145        
146        use Nucleotide::*;
147        assert_eq!(result.nucleotides, vec![
148            Thymine, Adenine, Guanine, 
149            Cytosine, Adenine, Thymine
150        ]);
151    }
152
153    #[test]
154    fn rna_example() {
155        let content = "cagGua";
156        let result = NucleicRna::build(content).unwrap();
157        
158        use Nucleotide::*;
159        assert_eq!(result.nucleotides, vec![
160            Cytosine, Adenine, Guanine,
161            Guanine, Uracil, Adenine            
162        ]);
163    }
164
165    #[test]
166    fn dna_to_rna() {
167        let content = "tAgCaT";
168        let dna = NucleicDna::build(content).unwrap();
169        let rna = NucleicRna::from(dna);
170
171        use Nucleotide::*;
172        assert_eq!(rna.nucleotides, vec![
173            Uracil, Adenine, Guanine, 
174            Cytosine, Adenine, Uracil
175        ]);
176    }
177
178    #[test]
179    fn rna_to_dna() {
180        let content = "cagGua";
181        let rna = NucleicRna::build(content).unwrap();
182        let dna = NucleicDna::from(rna);
183
184        use Nucleotide::*;
185        assert_eq!(dna.nucleotides, vec![
186            Cytosine, Adenine, Guanine,
187            Guanine, Thymine, Adenine            
188        ]);
189    }
190
191    #[test]
192    fn into_codons() {
193        let content = "cagGua";
194        let result = NucleicRna::build(content).unwrap();
195
196        let codons = result.into_codons();
197
198        use Nucleotide::*;
199        assert_eq!(codons, vec![
200            Codon([Cytosine, Adenine, Guanine]),
201            Codon([Guanine, Uracil, Adenine]),
202        ])
203    }
204}