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}