Skip to main content

omics_molecule/polymer/
dna.rs

1//! Deoxyribonucleic Acid.
2
3mod nucleotide;
4
5pub use nucleotide::Nucleotide;
6use thiserror::Error;
7
8/// An error related to a [`Molecule`].
9#[derive(Error, Debug)]
10pub enum Error {
11    /// An error when processing a [`Nucleotide`].
12    #[error(transparent)]
13    NucleotideError(#[from] nucleotide::Error),
14}
15
16/// A molecule representing Deoxyribonucleic Acid, otherwise known as DNA.
17#[derive(Debug)]
18pub struct Molecule(Vec<Nucleotide>);
19
20impl Molecule {
21    /// Gets the inner [`Vec<Nucleotide>`] by reference.
22    ///
23    /// # Examples
24    ///
25    /// ```
26    /// use omics_molecule::polymer::dna::Molecule;
27    ///
28    /// let m = "ACGT".parse::<Molecule>()?;
29    /// assert_eq!(m.inner().len(), 4);
30    ///
31    /// # Ok::<(), Box<dyn std::error::Error>>(())
32    /// ```
33    pub fn inner(&self) -> &Vec<Nucleotide> {
34        self.0.as_ref()
35    }
36
37    /// Consumes the [`Molecule`] and returns the inner [`Vec<Nucleotide>`].
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use omics_molecule::polymer::dna::Molecule;
43    /// use omics_molecule::polymer::dna::Nucleotide;
44    ///
45    /// let m = "ACGT".parse::<Molecule>()?;
46    /// let nucleotides = m.into_inner();
47    ///
48    /// assert_eq!(
49    ///     nucleotides,
50    ///     vec![Nucleotide::A, Nucleotide::C, Nucleotide::G, Nucleotide::T,]
51    /// );
52    ///
53    /// # Ok::<(), Box<dyn std::error::Error>>(())
54    /// ```
55    pub fn into_inner(self) -> Vec<Nucleotide> {
56        self.0
57    }
58
59    /// Gets the GC content of this [`Molecule`].
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// use omics_molecule::polymer::dna::Molecule;
65    ///
66    /// let m = "ACGT".parse::<Molecule>()?;
67    /// assert_eq!(m.gc_content(), 0.5);
68    ///
69    /// # Ok::<(), Box<dyn std::error::Error>>(())
70    /// ```
71    pub fn gc_content(&self) -> f32 {
72        let numerator = self
73            .0
74            .iter()
75            .filter(|n| *n == &Nucleotide::C || *n == &Nucleotide::G)
76            .count();
77
78        numerator as f32 / self.0.len() as f32
79    }
80}
81
82impl From<Vec<Nucleotide>> for Molecule {
83    fn from(v: Vec<Nucleotide>) -> Self {
84        Self(v)
85    }
86}
87
88impl std::str::FromStr for Molecule {
89    type Err = Error;
90
91    fn from_str(s: &str) -> Result<Self, Self::Err> {
92        s.chars()
93            .map(|c| Nucleotide::try_from(c).map_err(Error::NucleotideError))
94            .collect::<Result<Vec<_>, Error>>()
95            .map(Self::from)
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn it_creates_a_molecule_from_a_vec_of_nucleotides() {
105        let nucleotides = vec![Nucleotide::A, Nucleotide::C, Nucleotide::G, Nucleotide::T];
106
107        let dna = Molecule::from(nucleotides);
108        assert_eq!(dna.inner().len(), 4);
109    }
110
111    #[test]
112    fn it_parses_a_molecule_from_a_valid_string() -> Result<(), Box<dyn std::error::Error>> {
113        Ok("ACGT".parse::<Molecule>().map(|_| ())?)
114    }
115
116    #[test]
117    fn it_fails_to_parse_a_molecule_from_an_invalid_string() {
118        let err = "QQQQ".parse::<Molecule>().unwrap_err();
119        assert_eq!(err.to_string(), "invalid nucleotide `Q`");
120    }
121}