epimetheus_methylome/modtype.rs
1use anyhow::{Result, bail};
2use std::{fmt, str::FromStr};
3
4/// Represents a DNA base modification type.
5///
6/// This enum defines the types of modifications that can occur on DNA bases,
7/// including their associated codes for parsing and visualization.
8///
9/// # Variants
10/// - `SixMA`: N6-methyladenine (6mA), represented by the pileup code `a`.
11/// - `FiveMC`: 5-methylcytosine (5mC), represented by the pileup code `m`.
12/// - `FourMC`: 4-methylcytosine (4mC), represented by the pileup code `21839`.
13///
14/// # Examples
15/// ```
16/// use epimetheus_methylome::ModType;
17///
18/// let mod_type = ModType::SixMA;
19/// assert_eq!(mod_type.to_pileup_code(), "a");
20/// ```
21#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
22pub enum ModType {
23 SixMA,
24 FiveMC,
25 FourMC,
26}
27
28impl ModType {
29 /// Returns the pileup code corresponding to the modification type.
30 ///
31 /// Pileup codes are compact representations of modification types used
32 /// in sequencing data (or maybe just modkit):
33 /// - `SixMA` (6mA): `"a"`
34 /// - `FiveMC` (5mC): `"m"`
35 /// - `FourMC` (4mC): `"21839"`
36 ///
37 /// # Examples
38 /// ```
39 /// use epimetheus_methylome::ModType;
40 ///
41 /// let mod_type = ModType::FiveMC;
42 /// assert_eq!(mod_type.to_pileup_code(), "m");
43 /// ```
44 pub fn to_pileup_code(&self) -> &'static str {
45 match self {
46 ModType::SixMA => "a",
47 ModType::FiveMC => "m",
48 ModType::FourMC => "21839",
49 }
50 }
51
52 /// Passes the sam header tag to ModType
53 ///
54 /// Sam tags have the following and more for the methylation types:
55 /// MM:Z:{*}
56 /// - `A+a` (6mA): `"a"`
57 /// - `C+m` (5mC): `"m"`
58 /// - `C+21839` (4mC): `"21839"`
59 ///
60 /// # Examples
61 /// ```
62 /// use epimetheus_methylome::ModType;
63 ///
64 /// assert_eq!(ModType::from_sam_code('A', "a"), Some(ModType::SixMA));
65 /// ```
66 pub fn from_sam_code(base: char, modification: &str) -> Option<Self> {
67 // WARN I should add the other bases in the future
68 match (base, modification) {
69 ('A', "a") => Some(ModType::SixMA),
70 ('C', "m") => Some(ModType::FiveMC),
71 ('C', "21839") => Some(ModType::FourMC),
72 _ => None,
73 }
74 }
75}
76
77impl fmt::Display for ModType {
78 /// Formats the modification type for display purposes.
79 ///
80 /// Each modification type is represented in the format:
81 /// `<Modification Name> (<Pileup Code>)`.
82 ///
83 /// For example:
84 /// - `6mA (a)` for `SixMA`
85 /// - `5mC (m)` for `FiveMC`
86 /// - `4mC (21839)` for `FourMC`
87 ///
88 /// # Examples
89 /// ```
90 /// use epimetheus_methylome::ModType;
91 ///
92 /// let mod_type = ModType::FourMC;
93 /// assert_eq!(format!("{}", mod_type), "4mC (21839)");
94 /// ```
95 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96 match self {
97 ModType::SixMA => write!(f, "6mA (a)"),
98 ModType::FiveMC => write!(f, "5mC (m)"),
99 ModType::FourMC => write!(f, "4mC (21839)"),
100 }
101 }
102}
103
104/// Parses a modification type from a string.
105///
106/// The input string must match one of the following:
107/// - `"a"` for `SixMA` (6mA)
108/// - `"m"` for `FiveMC` (5mC)
109/// - `"21839"` for `FourMC` (4mC)
110///
111/// # Arguments
112/// - `mod_type`: A string slice representing the modification type.
113///
114/// # Returns
115/// - `Ok(ModType)` if the string matches a supported modification type.
116/// - `Err` if the string does not match any supported modification type.
117///
118/// # Examples
119/// ```
120/// use epimetheus_methylome::ModType;
121///
122/// let mod_type = "a".parse::<ModType>().unwrap();
123/// assert_eq!(mod_type, ModType::SixMA);
124///
125/// let invalid = "unsupported".parse::<ModType>();
126/// assert!(invalid.is_err());
127/// ```
128impl FromStr for ModType {
129 type Err = anyhow::Error;
130
131 fn from_str(s: &str) -> Result<Self, Self::Err> {
132 match s {
133 "a" => Ok(ModType::SixMA),
134 "m" => Ok(ModType::FiveMC),
135 "21839" => Ok(ModType::FourMC),
136 _ => bail!("Unsupported mod type: {}", s),
137 }
138 }
139}