yowl 0.1.0

Primitives for reading and writing the SMILES language
Documentation
use std::convert::TryInto;

use super::scanner::Scanner;
use crate::feature::Charge;

pub fn read_charge(scanner: &mut Scanner) -> Option<Charge> {
    match scanner.peek() {
        Some('+') => {
            scanner.pop();

            match fifteen(scanner) {
                Some(value) => Some(value.try_into().expect("charge")),
                None => match scanner.peek() {
                    Some('+') => {
                        scanner.pop();

                        Some(Charge::Two)
                    }
                    _ => Some(Charge::One),
                },
            }
        }
        Some('-') => {
            scanner.pop();

            match fifteen(scanner) {
                Some(value) => Some((-value).try_into().expect("charge")),
                None => match scanner.peek() {
                    Some('-') => {
                        scanner.pop();

                        Some(Charge::MinusTwo)
                    }
                    _ => Some(Charge::MinusOne),
                },
            }
        }
        _ => None,
    }
}

fn fifteen(scanner: &mut Scanner) -> Option<i8> {
    match scanner.peek() {
        Some('1'..='9') => Some(match scanner.pop() {
            Some('1') => match scanner.peek() {
                Some('1'..='5') => match scanner.pop() {
                    Some('1') => 11,
                    Some('2') => 12,
                    Some('3') => 13,
                    Some('4') => 14,
                    Some('5') => 15,
                    _ => 1,
                },
                _ => 1,
            },
            Some('2') => 2,
            Some('3') => 3,
            Some('4') => 4,
            Some('5') => 5,
            Some('6') => 6,
            Some('7') => 7,
            Some('8') => 8,
            Some('9') => 9,
            _ => unreachable!("fifteen"),
        }),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn none() {
        let mut scanner = Scanner::new("X");

        assert_eq!(read_charge(&mut scanner), None)
    }

    #[test]
    fn minus_x() {
        let mut scanner = Scanner::new("-X");

        assert_eq!(read_charge(&mut scanner), Some(Charge::MinusOne))
    }

    #[test]
    fn minus_2_x() {
        let mut scanner = Scanner::new("-1X");

        assert_eq!(read_charge(&mut scanner), Some(Charge::MinusOne))
    }

    #[test]
    fn minus_minus_x() {
        let mut scanner = Scanner::new("--X");

        assert_eq!(read_charge(&mut scanner), Some(Charge::MinusTwo))
    }

    #[test]
    fn minus_15_x() {
        let mut scanner = Scanner::new("-15X");

        assert_eq!(read_charge(&mut scanner), Some(Charge::MinusFifteen))
    }

    #[test]
    fn plus_x() {
        let mut scanner = Scanner::new("+X");

        assert_eq!(read_charge(&mut scanner), Some(Charge::One))
    }

    #[test]
    fn plus_plus_x() {
        let mut scanner = Scanner::new("++X");

        assert_eq!(read_charge(&mut scanner), Some(Charge::Two))
    }

    #[test]
    fn plus_2_x() {
        let mut scanner = Scanner::new("+2X");

        assert_eq!(read_charge(&mut scanner), Some(Charge::Two))
    }

    #[test]
    fn plus_15_x() {
        let mut scanner = Scanner::new("+15X");

        assert_eq!(read_charge(&mut scanner), Some(Charge::Fifteen))
    }
}