use crate::Element;
use super::{error::ReadError, missing_character::missing_character, scanner::Scanner};
use crate::feature::{AtomKind, Symbol};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AtomToken {
Aromatic(Element),
Aliphatic(Element),
Star,
}
fn next_atom_token(scanner: &mut Scanner) -> Result<Option<AtomToken>, ReadError> {
match scanner.peek() {
Some('*') => {
scanner.pop();
Ok(Some(AtomToken::Star))
}
Some('b') => {
scanner.pop();
Ok(Some(AtomToken::Aromatic(Element::B)))
}
Some('c') => {
scanner.pop();
Ok(Some(AtomToken::Aromatic(Element::C)))
}
Some('n') => {
scanner.pop();
Ok(Some(AtomToken::Aromatic(Element::N)))
}
Some('o') => {
scanner.pop();
Ok(Some(AtomToken::Aromatic(Element::O)))
}
Some('p') => {
scanner.pop();
Ok(Some(AtomToken::Aromatic(Element::P)))
}
Some('s') => {
scanner.pop();
Ok(Some(AtomToken::Aromatic(Element::S)))
}
Some('B') => {
scanner.pop();
if scanner.peek() == Some('r') {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::Br)))
} else {
Ok(Some(AtomToken::Aliphatic(Element::B)))
}
}
Some('C') => {
scanner.pop();
if scanner.peek() == Some('l') {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::Cl)))
} else {
Ok(Some(AtomToken::Aliphatic(Element::C)))
}
}
Some('T') => {
scanner.pop();
if scanner.peek() == Some('s') {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::Ts)))
} else {
Err(missing_character(scanner))
}
}
Some('A') => {
scanner.pop();
if scanner.peek() == Some('t') {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::At)))
} else {
Err(missing_character(scanner))
}
}
Some('N') => {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::N)))
}
Some('O') => {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::O)))
}
Some('P') => {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::P)))
}
Some('S') => {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::S)))
}
Some('F') => {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::F)))
}
Some('I') => {
scanner.pop();
Ok(Some(AtomToken::Aliphatic(Element::I)))
}
_ => Ok(None),
}
}
pub fn read_organic(scanner: &mut Scanner) -> Result<Option<AtomKind>, ReadError> {
if let Some(token) = next_atom_token(scanner)? {
let kind = match token {
AtomToken::Aromatic(element) => AtomKind::Symbol(Symbol::Aromatic(element)),
AtomToken::Aliphatic(a) => AtomKind::Symbol(Symbol::Aliphatic(a)),
AtomToken::Star => AtomKind::Symbol(Symbol::Star),
};
Ok(Some(kind))
} else {
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn b_x() {
let mut scanner = Scanner::new("Bx");
let atom = read_organic(&mut scanner);
assert_eq!(
atom,
Ok(Some(AtomKind::Symbol(Symbol::Aliphatic(Element::B))))
)
}
#[test]
fn c_x() {
let mut scanner = Scanner::new("Cx");
let atom = read_organic(&mut scanner);
assert_eq!(
atom,
Ok(Some(AtomKind::Symbol(Symbol::Aliphatic(Element::C))))
)
}
#[test]
fn scan_aromatics() {
let aromatic_strings = ["b", "c", "n", "o", "p", "s"];
let aromatic_results = [
Element::B,
Element::C,
Element::N,
Element::O,
Element::P,
Element::S,
];
for (inp, out) in aromatic_strings.iter().zip(aromatic_results) {
let mut scanner = Scanner::new(inp);
let atom = read_organic(&mut scanner);
assert_eq!(atom, Ok(Some(AtomKind::Symbol(Symbol::Aromatic(out)))))
}
}
#[test]
fn chlorine() {
let mut scanner = Scanner::new("Cl");
let atom = read_organic(&mut scanner);
assert_eq!(
atom,
Ok(Some(AtomKind::Symbol(Symbol::Aliphatic(Element::Cl))))
)
}
}