ff_structure/
dotbracket.rs

1use std::fmt;
2use std::ops::Deref;
3use std::ops::DerefMut;
4use std::convert::TryFrom;
5
6use crate::PairTable;
7use crate::MultiPairTable;
8use crate::MultiStruct;
9use crate::StrandPairTable;
10use crate::StructureError;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum DotBracket {
14    Unpaired, // '.'
15    Open,     // '('
16    Close,    // ')'
17    Break,    // '+' or '&'
18}
19
20impl TryFrom<char> for DotBracket {
21    type Error = StructureError;
22
23    fn try_from(c: char) -> Result<Self, Self::Error> {
24        match c {
25            '.' => Ok(DotBracket::Unpaired),
26            '(' => Ok(DotBracket::Open),
27            ')' => Ok(DotBracket::Close),
28            '+' | '&' => Ok(DotBracket::Break),
29            _ => Err(StructureError::InvalidToken(c.to_string(), "dot-bracket".into(), 0)),
30        }
31    }
32}
33
34impl From<DotBracket> for char {
35    fn from(db: DotBracket) -> Self {
36        match db {
37            DotBracket::Open => '(',
38            DotBracket::Close => ')',
39            DotBracket::Unpaired => '.',
40            DotBracket::Break => '+',
41        }
42    }
43}
44
45/// DotBracketVec is a compact representation of secondary structure. Note that
46/// the field is public, to allow unsafe modifications. Thus, DotBracketVecs can
47/// be malformed and should be converted using the TryFrom trait.
48#[derive(Debug, Clone, PartialEq, Eq, Hash)]
49pub struct DotBracketVec(pub Vec<DotBracket>);
50
51impl Deref for DotBracketVec {
52    type Target = [DotBracket];
53    fn deref(&self) -> &Self::Target {
54        &self.0
55    }
56}
57
58impl DerefMut for DotBracketVec {
59    fn deref_mut(&mut self) -> &mut Self::Target {
60        &mut self.0
61    }
62}
63
64impl TryFrom<&str> for DotBracketVec {
65    type Error = StructureError;
66
67    fn try_from(s: &str) -> Result<Self, Self::Error> {
68        let mut vec = Vec::with_capacity(s.len());
69        for (i, c) in s.chars().enumerate() {
70            match DotBracket::try_from(c) {
71                Ok(db) => vec.push(db),
72                Err(StructureError::InvalidToken(tok, src, _)) => {
73                    return Err(StructureError::InvalidToken(tok, src, i));
74                }
75                Err(e) => return Err(e),
76            }
77        }
78        Ok(DotBracketVec(vec))
79    }
80}
81
82impl From<&PairTable> for DotBracketVec {
83    fn from(pt: &PairTable) -> Self {
84        let mut result: Vec<DotBracket> = Vec::with_capacity(pt.len());
85        for (i, &j_opt) in pt.iter().enumerate() {
86            match j_opt {
87                None => result.push(DotBracket::Unpaired),
88                Some(j) if (j as usize) > i => result.push(DotBracket::Open),
89                Some(j) if (j as usize) < i => result.push(DotBracket::Close),
90                Some(j) if (j as usize) == i => {
91                    unreachable!("PairTable construction prevents self-pairing! ({})", i);
92                }
93                _ => unreachable!(),
94            }
95        }
96        DotBracketVec(result)
97    }
98}
99
100impl From<&MultiPairTable> for DotBracketVec {
101    fn from(mpt: &MultiPairTable) -> Self {
102        let mut db = Vec::with_capacity(mpt.len());
103
104        for (i, entry) in mpt.iter().enumerate() {
105            match entry {
106                MultiStruct::Unpaired => {
107                    db.push(DotBracket::Unpaired);
108                }
109                MultiStruct::StrandBreak => {
110                    db.push(DotBracket::Break);
111                }
112                MultiStruct::Paired(j) => {
113                    let j = *j as usize;
114                    if i < j {
115                        db.push(DotBracket::Open);
116                    } else {
117                        db.push(DotBracket::Close);
118                    }
119                }
120            }
121        }
122        DotBracketVec(db)
123    }
124}
125
126impl From<&StrandPairTable> for DotBracketVec {
127
128    fn from(pt: &StrandPairTable) -> Self {
129        let mut result: Vec<DotBracket> = Vec::with_capacity(pt.len() + pt.num_strands());
130
131        for (si, strand) in pt.iter().enumerate() {
132            for (di, &pair) in strand.iter().enumerate() {
133                match pair {
134                    None => result.push(DotBracket::Unpaired),
135                    Some((sj, dj)) => {
136                        let sj = sj as usize;
137                        let dj = dj as usize;
138                        if (sj, dj) > (si, di) {
139                            result.push(DotBracket::Open);
140                        } else if (sj, dj) < (si, di) {
141                            result.push(DotBracket::Close);
142                        } else {
143                            panic!("Invalid self-pairing at strand {si}, domain {di}");
144                        }
145                    }
146                }
147            }
148            // NOTE:: pushes strand break at the end of the DotBracketVec,
149            // intentionally!
150            result.push(DotBracket::Break);
151        }
152
153        DotBracketVec(result)
154    }
155}
156
157impl fmt::Display for DotBracketVec {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        for db in &self.0 {
160            write!(f, "{}", char::from(*db))?;
161        }
162        Ok(())
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn test_dot_bracket_from_char() {
172        assert_eq!(DotBracket::try_from('.').unwrap(), DotBracket::Unpaired);
173        assert_eq!(DotBracket::try_from('(').unwrap(), DotBracket::Open);
174        assert_eq!(DotBracket::try_from(')').unwrap(), DotBracket::Close);
175    }
176
177    #[test]
178    fn test_char_from_dot_bracket() {
179        assert_eq!(char::from(DotBracket::Unpaired), '.');
180        assert_eq!(char::from(DotBracket::Open), '(');
181        assert_eq!(char::from(DotBracket::Close), ')');
182    }
183
184    #[test]
185    fn test_dot_bracket_from_invalid_char() {
186        let res = DotBracket::try_from('x');
187        assert!(matches!(res, Err(StructureError::InvalidToken(_, src, _)) if src == "dot-bracket"));
188    }
189
190    #[test]
191    fn test_dot_bracket_vec_from_str() {
192        let dbv = DotBracketVec::try_from("(.).").unwrap();
193        assert_eq!(format!("{}", dbv), "(.).");
194        assert_eq!(dbv.len(), 4);
195        assert_eq!(dbv[0], DotBracket::Open);
196        assert_eq!(dbv[1], DotBracket::Unpaired);
197        assert_eq!(dbv[2], DotBracket::Close);
198        assert_eq!(dbv[3], DotBracket::Unpaired);
199    }
200
201    #[test]
202    fn test_dot_bracket_vec_from_pair_table() {
203        let pt = PairTable::try_from("((..))").unwrap();
204        let dbv = DotBracketVec::from(&pt);
205        assert_eq!(format!("{}", dbv), "((..))");
206    }
207
208    #[test]
209    fn test_dot_bracket_vec_from_multi_pair_table_hack() {
210        let pt = StrandPairTable::try_from("((..))+").unwrap();
211        let dbv = DotBracketVec::from(&pt);
212        assert_eq!(format!("{}", dbv), "((..))+");
213    }
214
215    #[test]
216    fn test_dot_bracket_vec_from_multi_pair_table() {
217        let pt = StrandPairTable::try_from("((..)+)").unwrap();
218        let dbv = DotBracketVec::from(&pt);
219        assert_eq!(format!("{}", dbv), "((..)+)+");
220    }
221
222}
223