nanalogue_core/utils/
restrict_mod_called_strand.rs

1//! `RestrictModCalledStrand` struct for strand-specific modification filtering
2//! Controls whether to use modifications from basecalled or complement strand
3
4use crate::Error;
5use serde::{Deserialize, Serialize};
6use std::fmt;
7use std::str::FromStr;
8
9/// Struct to mark if we want data only from the `+`
10/// or the `-` mod-called strand.
11/// NOTE: this doesn't mean the strand from the alignment,
12/// but the strand from the modification data i.e. the strand
13/// in the MM tag e.g. T+T, T-a where the strand is `+` and `-`
14/// respectively. This denotes if mod data is from the basecalled
15/// strand or the complementary strand. To not confuse users of the
16/// command line interface, we allow creation of this from a string
17/// `bc` or `bc_comp` i.e. basecalled or basecalled complement instead
18/// of `+` and `-`, which may be mistaken for the alignment strand.
19#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
20pub struct RestrictModCalledStrand(bool);
21
22/// default mod strand restriction is +
23impl Default for RestrictModCalledStrand {
24    fn default() -> Self {
25        RestrictModCalledStrand(true)
26    }
27}
28
29/// Set states according to if we receive `bc` or `bc_comp`
30///
31/// ```
32/// use nanalogue_core::RestrictModCalledStrand;
33/// use std::str::FromStr;
34/// assert_eq!('+', char::from(RestrictModCalledStrand::from_str("bc")?));
35/// assert_eq!('-', char::from(RestrictModCalledStrand::from_str("bc_comp")?));
36/// # Ok::<(), nanalogue_core::Error>(())
37/// ```
38impl FromStr for RestrictModCalledStrand {
39    type Err = Error;
40
41    /// Parse a string to create a `RestrictModCalledStrand` ("bc or "`bc_comp`")
42    fn from_str(val_str: &str) -> Result<Self, Self::Err> {
43        match val_str {
44            "bc" => Ok(RestrictModCalledStrand(true)),
45            "bc_comp" => Ok(RestrictModCalledStrand(false)),
46            _ => Err(Error::InvalidState(
47                "Please specify bc or bc_comp for mod-called strand!".to_string(),
48            )),
49        }
50    }
51}
52
53/// Prints the true state as `+` and the false state as `-`
54///
55/// ```
56/// use nanalogue_core::RestrictModCalledStrand;
57/// use std::fmt::Display;
58/// use std::str::FromStr;
59/// assert_eq!("+", format!("{}",RestrictModCalledStrand::from_str("bc")?));
60/// assert_eq!("-", format!("{}",RestrictModCalledStrand::from_str("bc_comp")?));
61/// # Ok::<(), nanalogue_core::Error>(())
62/// ```
63impl fmt::Display for RestrictModCalledStrand {
64    /// converts to string for display i.e. "+" or "-"
65    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66        write!(f, "{}", char::from(*self))
67    }
68}
69
70/// Converts the true state to the `+` character, and false to `-`
71///
72/// ```
73/// use nanalogue_core::RestrictModCalledStrand;
74/// use std::str::FromStr;
75/// assert_eq!('+', char::from(RestrictModCalledStrand::from_str("bc")?));
76/// assert_eq!('-', char::from(RestrictModCalledStrand::from_str("bc_comp")?));
77/// # Ok::<(), nanalogue_core::Error>(())
78/// ```
79impl From<RestrictModCalledStrand> for char {
80    /// converts to char
81    fn from(val: RestrictModCalledStrand) -> char {
82        match val {
83            RestrictModCalledStrand(true) => '+',
84            RestrictModCalledStrand(false) => '-',
85        }
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn restrict_mod_called_strand_basic() {
95        let bc = RestrictModCalledStrand::from_str("bc").expect("should parse");
96        assert_eq!(format!("{bc}"), "+");
97        assert_eq!(char::from(bc), '+');
98
99        let bc_comp = RestrictModCalledStrand::from_str("bc_comp").expect("should parse");
100        assert_eq!(format!("{bc_comp}"), "-");
101        assert_eq!(char::from(bc_comp), '-');
102    }
103
104    #[test]
105    fn restrict_mod_called_strand_errors() {
106        assert!(matches!(
107            RestrictModCalledStrand::from_str("invalid"),
108            Err(Error::InvalidState(_))
109        ));
110        assert!(matches!(
111            RestrictModCalledStrand::from_str(""),
112            Err(Error::InvalidState(_))
113        ));
114        assert!(matches!(
115            RestrictModCalledStrand::from_str("+"),
116            Err(Error::InvalidState(_))
117        )); // Should be "bc", not "+"
118    }
119
120    #[test]
121    fn restrict_mod_called_strand_default() {
122        let default_strand = RestrictModCalledStrand::default();
123        assert_eq!(char::from(default_strand), '+');
124    }
125}