Skip to main content

read_structure/
segment_type.rs

1//! Segment Types
2//!
3//! Type [`SegmentType`] represents the types of segments that can show
4//! up in a read structure ([`crate::read_structure::ReadStructure`]: trait.ReadStructure).
5
6use std::{convert::TryFrom, mem, str::FromStr};
7
8use strum::IntoEnumIterator;
9use strum_macros::EnumIter;
10
11use crate::ReadStructureError;
12
13/// The `SegmentType` type. See [the module level documentation](self) for more.
14#[non_exhaustive]
15#[derive(Debug, Copy, Clone, EnumIter, PartialEq, Eq, Hash, PartialOrd, Ord)]
16#[repr(u8)]
17pub enum SegmentType {
18    /// Template: the bases in the segment are reads of template (e.g. genomic dna, rna, etc.)
19    Template = b'T',
20    /// Sample Barcode: the bases in the segment are an index sequence used to identify the sample being sequenced
21    SampleBarcode = b'B',
22    /// Molecular Barcode: the bases in the segment are an index sequence used to identify the unique source molecule being sequence (i.e. a UMI)
23    MolecularBarcode = b'M',
24    /// Skip: the bases in the segment should be skipped or ignored, for example if they are monotemplate sequence generated by the library preparation
25    Skip = b'S',
26    /// Cellular Barcode: the bases in the segment are an index sequence used to identify the unique cell being sequenced
27    CellularBarcode = b'C',
28}
29
30impl SegmentType {
31    /// Returns the character representation of this segment type.
32    pub fn value(&self) -> char {
33        let value = *self as u8;
34        value as char
35    }
36}
37
38impl TryFrom<char> for SegmentType {
39    type Error = ReadStructureError;
40
41    /// Returns the segment type given the character representation.
42    ///
43    /// # Errors
44    ///
45    /// - If `SegmentType` not valid
46    fn try_from(value: char) -> Result<Self, Self::Error> {
47        match value {
48            'T' => Ok(SegmentType::Template),
49            'B' => Ok(SegmentType::SampleBarcode),
50            'M' => Ok(SegmentType::MolecularBarcode),
51            'S' => Ok(SegmentType::Skip),
52            'C' => Ok(SegmentType::CellularBarcode),
53            _ => Err(ReadStructureError::ReadSegmentTypeInvalid(value)),
54        }
55    }
56}
57
58impl TryFrom<u8> for SegmentType {
59    type Error = ReadStructureError;
60
61    /// Returns the segment type given the byte representation.
62    ///
63    /// # Errors
64    ///
65    /// - If `SegmentType` not valid
66    fn try_from(value: u8) -> Result<Self, Self::Error> {
67        Self::try_from(value as char)
68    }
69}
70
71impl FromStr for SegmentType {
72    type Err = ReadStructureError;
73
74    /// Returns the segment type given the string representation.
75    ///
76    /// # Errors
77    ///
78    /// - If `SegmentType` not valid
79    fn from_str(value: &str) -> Result<Self, Self::Err> {
80        if value.len() == 1 {
81            Self::try_from(value.chars().next().unwrap())
82        } else {
83            Err(ReadStructureError::ReadSegmentTypeStringInvalid(value.to_owned()))
84        }
85    }
86}
87
88#[cfg(test)]
89mod test {
90    use std::convert::TryFrom;
91    use std::str::FromStr;
92
93    use crate::{ReadStructureError, segment_type::SegmentType};
94    use strum::IntoEnumIterator;
95
96    #[test]
97    fn test_segment_type_round_trip() -> Result<(), ReadStructureError> {
98        assert_eq!(SegmentType::iter().len(), 5);
99        for tpe in SegmentType::iter() {
100            assert_eq!(SegmentType::try_from(tpe.value())?, tpe);
101        }
102        Ok(())
103    }
104
105    #[test]
106    fn test_invalid_segment_type() {
107        assert!(SegmentType::try_from(b'G').is_err());
108    }
109
110    #[test]
111    fn test_segment_type_from_str() -> Result<(), ReadStructureError> {
112        let segment_types_char: [char; 5] = ['T', 'B', 'M', 'S', 'C'];
113        let segment_types_str: [&str; 5] = ["T", "B", "M", "S", "C"];
114        let mut iter = segment_types_str.iter().zip(segment_types_char.iter());
115        for (s, c) in iter {
116            assert_eq!(SegmentType::from_str(s)?, SegmentType::try_from(*c)?);
117        }
118        Ok(())
119    }
120
121    #[test]
122    fn test_invalid_segment_type_string() {
123        assert!(SegmentType::from_str("").is_err());
124        assert!(SegmentType::from_str("GG").is_err());
125        assert!(SegmentType::from_str("TG").is_err());
126        assert!(!SegmentType::from_str("T").is_err());
127    }
128}