rustgym 0.2.0

rustgym solutions
Documentation
struct Solution;

use std::collections::BTreeMap;
use std::iter::Peekable;
use std::str::Chars;
use std::vec::IntoIter;

#[derive(Debug)]
enum Tok {
    Num(usize),
    Name(String),
    Op(char),
}

impl Solution {
    fn count_of_atoms(formula: String) -> String {
        let mut it = formula.chars().peekable();
        let tokens = Self::parse_tokens(&mut it);
        let count: BTreeMap<String, usize> = Self::parse(&mut tokens.into_iter().peekable());
        count
            .into_iter()
            .map(|(k, v)| if v > 1 { format!("{}{}", k, v) } else { k })
            .collect()
    }

    fn parse(it: &mut Peekable<IntoIter<Tok>>) -> BTreeMap<String, usize> {
        let mut res: BTreeMap<String, usize> = BTreeMap::new();
        loop {
            match it.peek() {
                Some(Tok::Name(_)) => {
                    if let Some(Tok::Name(s)) = it.next() {
                        let x = if let Some(&Tok::Num(x)) = it.peek() {
                            it.next();
                            x
                        } else {
                            1
                        };
                        *res.entry(s).or_default() += x;
                    }
                }
                Some(&Tok::Op('(')) => {
                    it.next();
                    let inside = Self::parse(it);
                    it.next();
                    let x = if let Some(&Tok::Num(x)) = it.peek() {
                        it.next();
                        x
                    } else {
                        1
                    };
                    for (k, v) in inside {
                        *res.entry(k).or_default() += v * x;
                    }
                }
                Some(&Tok::Op(')')) | None => {
                    break;
                }
                _ => panic!(),
            }
        }
        res
    }

    fn parse_tokens(it: &mut Peekable<Chars>) -> Vec<Tok> {
        let mut res = vec![];
        while let Some(c) = it.next() {
            match c {
                '(' | ')' => res.push(Tok::Op(c)),
                '0'..='9' => {
                    let mut x = (c as u8 - b'0') as usize;
                    while let Some('0'..='9') = it.peek() {
                        x *= 10;
                        let y = (it.next().unwrap() as u8 - b'0') as usize;
                        x += y;
                    }
                    res.push(Tok::Num(x));
                }
                'A'..='Z' => {
                    let mut s = "".to_string();
                    s.push(c);
                    while let Some('a'..='z') = it.peek() {
                        s.push(it.next().unwrap());
                    }
                    res.push(Tok::Name(s));
                }
                _ => panic!(),
            }
        }
        res
    }
}

#[test]
fn test() {
    let formula = "H2O".to_string();
    let res = "H2O".to_string();
    assert_eq!(Solution::count_of_atoms(formula), res);
    let formula = "Mg(OH)2".to_string();
    let res = "H2MgO2".to_string();
    assert_eq!(Solution::count_of_atoms(formula), res);
    let formula = "K4(ON(SO3)2)2".to_string();
    let res = "K4N2O14S4".to_string();
    assert_eq!(Solution::count_of_atoms(formula), res);
}