noodles_fasta/record/
sequence.rs

1//! FASTA record sequence.
2
3pub mod complement;
4
5pub use self::complement::Complement;
6
7use std::ops::Index;
8
9use bytes::Bytes;
10use noodles_core::{position::SequenceIndex, region::Interval};
11
12/// A FASTA record sequence.
13#[derive(Clone, Debug, Default, Eq, PartialEq)]
14pub struct Sequence(Bytes);
15
16impl Sequence {
17    /// Returns the length of the sequence.
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use noodles_fasta::record::Sequence;
23    /// let sequence = Sequence::default();
24    /// assert_eq!(sequence.len(), 0);
25    /// ```
26    pub fn len(&self) -> usize {
27        self.0.len()
28    }
29
30    /// Returns whether the sequence is empty.
31    ///
32    /// # Examples
33    ///
34    /// ```
35    /// use noodles_fasta::record::Sequence;
36    /// let sequence = Sequence::default();
37    /// assert!(sequence.is_empty());
38    /// ```
39    pub fn is_empty(&self) -> bool {
40        self.0.is_empty()
41    }
42
43    /// Returns a reference to a base at or slice of bases between the given index.
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use noodles_core::Position;
49    /// use noodles_fasta::record::Sequence;
50    ///
51    /// let sequence = Sequence::from(b"ACGT".to_vec());
52    ///
53    /// let start = Position::try_from(2)?;
54    /// assert_eq!(sequence.get(start), Some(&b'C'));
55    ///
56    /// assert_eq!(sequence.get(start..), Some(&b"CGT"[..]));
57    ///
58    /// let end = Position::try_from(3)?;
59    /// assert_eq!(sequence.get(start..=end), Some(&b"CG"[..]));
60    /// # Ok::<_, noodles_core::position::TryFromIntError>(())
61    /// ```
62    pub fn get<I>(&self, index: I) -> Option<&I::Output>
63    where
64        I: SequenceIndex<u8>,
65    {
66        index.get(self.as_ref())
67    }
68
69    /// Returns a subset of the sequence within the given range.
70    ///
71    /// Unlike [`Self::get`], this returns the slice as a [`Sequence`].
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use noodles_core::Position;
77    /// use noodles_fasta::record::Sequence;
78    ///
79    /// let sequence = Sequence::from(b"ACGT".to_vec());
80    ///
81    /// let start = Position::try_from(2)?;
82    /// let end = Position::try_from(3)?;
83    /// let actual = sequence.slice(start..=end);
84    ///
85    /// let expected = Sequence::from(b"CG".to_vec());
86    ///
87    /// assert_eq!(actual, Some(expected));
88    /// # Ok::<_, noodles_core::position::TryFromIntError>(())
89    /// ```
90    pub fn slice<I>(&self, interval: I) -> Option<Self>
91    where
92        I: Into<Interval>,
93    {
94        let interval = interval.into();
95
96        let start = interval
97            .start()
98            .map(|position| usize::from(position) - 1)
99            .unwrap_or(usize::MIN);
100
101        let end = interval.end().map(usize::from).unwrap_or(self.len());
102
103        if start <= end && end <= self.len() {
104            let buf = self.0.slice(start..end);
105            Some(Self::from(buf))
106        } else {
107            None
108        }
109    }
110
111    /// Returns an iterator that complements the sequence.
112    ///
113    /// # Examples
114    ///
115    /// ## Complement a sequence
116    ///
117    /// ```
118    /// use noodles_fasta::record::Sequence;
119    /// let sequence = Sequence::from(b"ACGT".to_vec());
120    /// let actual: Sequence = sequence.complement().collect::<Result<_, _>>()?;
121    /// let expected = Sequence::from(b"TGCA".to_vec());
122    /// assert_eq!(actual, expected);
123    /// # Ok::<_, noodles_fasta::record::sequence::complement::ComplementError>(())
124    /// ```
125    ///
126    /// ## Reverse complement a sequence
127    ///
128    /// ```
129    /// use noodles_fasta::record::Sequence;
130    /// let sequence = Sequence::from(b"ACGT".to_vec());
131    /// let actual: Sequence = sequence.complement().rev().collect::<Result<_, _>>()?;
132    /// let expected = sequence.clone();
133    /// assert_eq!(actual, expected);
134    /// # Ok::<_, noodles_fasta::record::sequence::complement::ComplementError>(())
135    /// ```
136    pub fn complement(&self) -> Complement<'_> {
137        Complement::new(self.0.iter())
138    }
139}
140
141impl AsRef<[u8]> for Sequence {
142    fn as_ref(&self) -> &[u8] {
143        &self.0
144    }
145}
146
147impl From<Vec<u8>> for Sequence {
148    fn from(data: Vec<u8>) -> Self {
149        Self(Bytes::from(data))
150    }
151}
152
153impl From<Bytes> for Sequence {
154    fn from(data: Bytes) -> Self {
155        Self(data)
156    }
157}
158
159impl FromIterator<u8> for Sequence {
160    fn from_iter<T>(iter: T) -> Self
161    where
162        T: IntoIterator<Item = u8>,
163    {
164        let mut buf = Vec::new();
165        buf.extend(iter);
166        Self::from(buf)
167    }
168}
169
170impl<I> Index<I> for Sequence
171where
172    I: SequenceIndex<u8>,
173{
174    type Output = I::Output;
175
176    fn index(&self, index: I) -> &Self::Output {
177        index.index(self.as_ref())
178    }
179}