purr/read/
read_bracket.rs

1use std::convert::TryInto;
2
3use crate::feature::{ AtomKind, VirtualHydrogen, Number };
4use super::{
5    scanner::Scanner,
6    read_symbol,
7    read_charge,
8    read_configuration,
9    missing_character,
10    Error
11};
12
13pub fn read_bracket(scanner: &mut Scanner) -> Result<Option<AtomKind>, Error> {
14    if let Some('[') = scanner.peek() {
15        scanner.pop();
16    } else {
17        return Ok(None);
18    }
19
20    let isotope = read_isotope(scanner)?;
21    let symbol = read_symbol(scanner)?;
22    let configuration = read_configuration(scanner)?;
23    let hcount = read_hcount(scanner)?;
24    let charge = read_charge(scanner)?;
25    let map = read_map(scanner)?;
26
27    match scanner.peek() {
28        Some(']') => {
29            scanner.pop();
30            
31            Ok(Some(AtomKind::Bracket {
32                isotope, symbol, configuration, hcount, charge, map
33            }))
34        },
35        None => Err(Error::EndOfLine),
36        _ => Err(Error::Character(scanner.cursor()))
37    }
38}
39
40fn read_hcount(
41    scanner: &mut Scanner
42) -> Result<Option<VirtualHydrogen>, Error> {
43    match scanner.peek() {
44        Some('H') => {
45            scanner.pop();
46
47            match scanner.peek() {
48                Some('0'..='9') => match scanner.pop() {
49                    Some('0') => Ok(Some(VirtualHydrogen::H0)),
50                    Some('1') => Ok(Some(VirtualHydrogen::H1)),
51                    Some('2') => Ok(Some(VirtualHydrogen::H2)),
52                    Some('3') => Ok(Some(VirtualHydrogen::H3)),
53                    Some('4') => Ok(Some(VirtualHydrogen::H4)),
54                    Some('5') => Ok(Some(VirtualHydrogen::H5)),
55                    Some('6') => Ok(Some(VirtualHydrogen::H6)),
56                    Some('7') => Ok(Some(VirtualHydrogen::H7)),
57                    Some('8') => Ok(Some(VirtualHydrogen::H8)),
58                    Some('9') => Ok(Some(VirtualHydrogen::H9)),
59                    _ => Ok(Some(VirtualHydrogen::H1))
60                },
61                _ => Ok(Some(VirtualHydrogen::H1))
62            }
63        },
64        _ => Ok(None)
65    }
66}
67
68fn read_isotope(scanner: &mut Scanner) -> Result<Option<Number>, Error> {
69    let mut digits = String::new();
70
71    for _ in 0..3 {
72        match scanner.peek() {
73            Some('0'..='9') => digits.push(*scanner.pop().expect("digit")),
74            _ => break
75        }
76    }
77
78    if digits.is_empty() {
79        Ok(None)
80    } else {
81        Ok(Some(digits.try_into().expect("number")))
82    }
83}
84
85fn read_map(scanner: &mut Scanner) -> Result<Option<Number>, Error> {
86    match scanner.peek() {
87        Some(':') => {
88            scanner.pop();
89
90            let mut digits = String::new();
91
92            match scanner.pop() {
93                Some(next) => if next.is_ascii_digit() {
94                    digits.push(*next);
95                } else {
96                    return Err(Error::Character(scanner.cursor() - 1))
97                },
98                None => return Err(missing_character(scanner))
99            }
100 
101            for _ in 0..2 {
102                match scanner.peek() {
103                    Some('0'..='9') =>
104                        digits.push(*scanner.pop().expect("digit")),
105                    _ => break
106                }
107            }
108
109            Ok(Some(digits.try_into().expect("number")))
110        },
111        _ => Ok(None)
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use std::convert::TryInto;
118    use pretty_assertions::assert_eq;
119    use crate::feature::{ BracketSymbol, Configuration, BracketAromatic, Charge };
120    use super::*;
121
122    #[test]
123    fn overflow_map() {
124        let mut scanner = Scanner::new("[*:1000]");
125
126        assert_eq!(read_bracket(&mut scanner), Err(Error::Character(6)))
127    }
128
129    #[test]
130    fn overflow_isotope() {
131        let mut scanner = Scanner::new("[1000U]");
132
133        assert_eq!(read_bracket(&mut scanner), Err(Error::Character(4)))
134    }
135
136    #[test]
137    fn bracket_invalid() {
138        let mut scanner = Scanner::new("[Q]");
139
140        assert_eq!(read_bracket(&mut scanner), Err(Error::Character(1)))
141    }
142
143    #[test]
144    fn no_close() {
145        let mut scanner = Scanner::new("[C");
146
147        assert_eq!(read_bracket(&mut scanner), Err(Error::EndOfLine))
148    }
149
150    #[test]
151    fn colon_but_no_map() {
152        let mut scanner = Scanner::new("[C:]");
153
154        assert_eq!(read_bracket(&mut scanner), Err(Error::Character(3)))
155    }
156
157    #[test]
158    fn colon_eol() {
159        let mut scanner = Scanner::new("[C:");
160
161        assert_eq!(read_bracket(&mut scanner), Err(Error::EndOfLine))
162    }
163
164    #[test]
165    fn no_open() {
166        let mut scanner = Scanner::new("?");
167
168        assert_eq!(read_bracket(&mut scanner), Ok(None))
169    }
170
171    #[test]
172    fn star() {
173        let mut scanner = Scanner::new("[*]");
174
175        assert_eq!(read_bracket(&mut scanner), Ok(Some(AtomKind::Bracket {
176            isotope: None,
177            symbol: BracketSymbol::Star,
178            configuration: None,
179            hcount: None,
180            charge: None,
181            map: None
182        })))
183    }
184
185    #[test]
186    fn star_isotope() {
187        let mut scanner = Scanner::new("[999*]");
188
189        assert_eq!(read_bracket(&mut scanner), Ok(Some(AtomKind::Bracket {
190            isotope: Some(999.try_into().unwrap()),
191            symbol: BracketSymbol::Star,
192            configuration: None,
193            hcount: None,
194            charge: None,
195            map: None
196        })))
197    }
198
199    #[test]
200    fn star_configuration() {
201        let mut scanner = Scanner::new("[*@]");
202
203        assert_eq!(read_bracket(&mut scanner), Ok(Some(AtomKind::Bracket {
204            isotope: None,
205            symbol: BracketSymbol::Star,
206            configuration: Some(Configuration::TH1),
207            hcount: None,
208            charge: None,
209            map: None
210        })))
211    }
212
213    #[test]
214    fn star_hcount() {
215        let mut scanner = Scanner::new("[*H2]");
216
217        assert_eq!(read_bracket(&mut scanner), Ok(Some(AtomKind::Bracket {
218            isotope: None,
219            symbol: BracketSymbol::Star,
220            configuration: None,
221            hcount: Some(VirtualHydrogen::H2),
222            charge: None,
223            map: None
224        })))
225    }
226
227    #[test]
228    fn star_charge() {
229        let mut scanner = Scanner::new("[*+]");
230
231        assert_eq!(read_bracket(&mut scanner), Ok(Some(AtomKind::Bracket {
232            isotope: None,
233            symbol: BracketSymbol::Star,
234            configuration: None,
235            hcount: None,
236            charge: Some(Charge::One),
237            map: None
238        })))
239    }
240
241    #[test]
242    fn star_map() {
243        let mut scanner = Scanner::new("[*:999]");
244
245        assert_eq!(read_bracket(&mut scanner), Ok(Some(AtomKind::Bracket {
246            isotope: None,
247            symbol: BracketSymbol::Star,
248            configuration: None,
249            hcount: None,
250            charge: None,
251            map: Some(999u16.try_into().unwrap())
252        })))
253    }
254
255    #[test]
256    fn bracket_aromatic_charge() {
257        let mut scanner = Scanner::new("[s+]");
258
259        assert_eq!(read_bracket(&mut scanner), Ok(Some(AtomKind::Bracket {
260            isotope: None,
261            symbol: BracketSymbol::Aromatic(BracketAromatic::S),
262            configuration: None,
263            hcount: None,
264            charge: Some(Charge::One),
265            map: None
266        })))
267    }
268}