pekzep_numeral 0.2.0

See https://github.com/jurliyuuri/ckka#%E6%95%B0%E3%81%AE%E8%A1%A8%E7%8F%BE%E6%96%B9%E6%B3%95 and http://jurliyuuri.github.io/lin-marn/lin-marn.html#id317 for more context.
Documentation
mod tests;

pub fn chars_to_num(s: &[char]) -> Option<i64> {
    match s {
        [''] => Some(0),
        ['', tail @ ..] => positive(tail).map(|a| -a),
        simple => positive(simple),
    }
}

pub fn str_to_num(s: &str) -> Option<i64> {
    chars_to_num(&s.chars().collect::<Vec<_>>())
}

fn less_than_10(s: char) -> Option<i64> {
    match s {
        '' => Some(1),
        '' => Some(2),
        '' => Some(3),
        '' => Some(4),
        '' => Some(5),
        '' => Some(6),
        '' => Some(7),
        '' => Some(8),
        '' => Some(9),
        _ => None,
    }
}

fn less_than_100(s: &[char]) -> Option<i64> {
    match s {
        [''] => Some(10),
        [digit] => less_than_10(*digit),
        [digit, ''] => less_than_10(*digit).map(|a| a * 10),
        ['', digit] => less_than_10(*digit).map(|a| a + 10),
        [digit1, '', digit2] => {
            let d1 = less_than_10(*digit1)?;
            let d2 = less_than_10(*digit2)?;
            Some(d1 * 10 + d2)
        }
        _ => None,
    }
}

fn less_than_100_nun1_elided(s: &[char]) -> Option<i64> {
    match s {
        [''] => Some(10),
        [digit] => less_than_10(*digit),
        [digit, ''] => less_than_10(*digit).map(|a| a * 10),
        ['', digit] => less_than_10(*digit).map(|a| a + 10),
        [digit1, digit2] => {
            let d1 = less_than_10(*digit1)?;
            let d2 = less_than_10(*digit2)?;
            Some(d1 * 10 + d2)
        }
        _ => None,
    }
}

#[allow(clippy::many_single_char_names)]
fn less_than_10000_0000(input: &[char]) -> Option<i64> {
    match input {
        [''] => Some(10000),
        [head @ .., ''] => less_than_10000_or_elided(head).map(|w| w * 10000),
        ['', tail @ ..] => Some(10000 + less_than_10000_or_elided(tail)?),

        [a, '', tail @ ..] => {
            Some(less_than_10000_or_elided(&[*a])? * 10000 + less_than_10000_or_elided(tail)?)
        }
        [a, b, '', tail @ ..] => {
            Some(less_than_10000_or_elided(&[*a, *b])? * 10000 + less_than_10000_or_elided(tail)?)
        }
        [a, b, c, '', tail @ ..] => Some(
            less_than_10000_or_elided(&[*a, *b, *c])? * 10000 + less_than_10000_or_elided(tail)?,
        ),
        [a, b, c, d, '', tail @ ..] => Some(
            less_than_10000_or_elided(&[*a, *b, *c, *d])? * 10000
                + less_than_10000_or_elided(tail)?,
        ),
        [a, b, c, d, e, '', tail @ ..] => Some(
            less_than_10000_or_elided(&[*a, *b, *c, *d, *e])? * 10000
                + less_than_10000_or_elided(tail)?,
        ),
        _ => less_than_10000(input),
    }
}

pub fn less_than_10000_or_elided(s: &[char]) -> Option<i64> {
    match less_than_100_nun1_elided(s) {
        Some(n) => Some(n),
        None => less_than_10000(s),
    }
}

pub fn less_than_10000_0000_or_elided(s: &[char]) -> Option<i64> {
    match less_than_100_nun1_elided(s) {
        Some(n) => Some(n),
        None => less_than_10000_0000(s),
    }
}

pub fn less_than_10000(s: &[char]) -> Option<i64> {
    match s {
        [''] => Some(100),
        [a, ''] => less_than_100_nun1_elided(&[*a]).map(|w| w * 100),
        [a, b, ''] => less_than_100_nun1_elided(&[*a, *b]).map(|w| w * 100),

        ['', c] => less_than_100_nun1_elided(&[*c]).map(|a| 100 + a),
        ['', c, d] => less_than_100_nun1_elided(&[*c, *d]).map(|a| 100 + a),
        ['', c, d, e] => less_than_100(&[*c, *d, *e]).map(|a| 100 + a), // only for pure hundred
        [a, '', tail @ ..] => {
            Some(less_than_100_nun1_elided(&[*a])? * 100 + less_than_100_nun1_elided(tail)?)
        }
        [a, b, '', tail @ ..] => {
            Some(less_than_100_nun1_elided(&[*a, *b])? * 100 + less_than_100_nun1_elided(tail)?)
        }
        _ => less_than_100(s),
    }
}

fn positive(s: &[char]) -> Option<i64> {
    match s {
        [''] => Some(1_0000_0000),
        [head @ .., ''] => less_than_10000_0000_or_elided(head).map(|w| w * 1_0000_0000),
        ['', tail @ ..] => Some(1_0000_0000 + less_than_10000_0000_or_elided(tail)?),

        [a, '', tail @ ..] => Some(
            less_than_10000_0000_or_elided(&[*a])? * 1_0000_0000
                + less_than_10000_0000_or_elided(tail)?,
        ),
        [a, b, '', tail @ ..] => Some(
            less_than_10000_0000_or_elided(&[*a, *b])? * 1_0000_0000
                + less_than_10000_0000_or_elided(tail)?,
        ),
        [a, b, c, '', tail @ ..] => Some(
            less_than_10000_0000_or_elided(&[*a, *b, *c])? * 1_0000_0000
                + less_than_10000_0000_or_elided(tail)?,
        ),
        // only need to handle till 21億
        _ => less_than_10000_0000(s),
    }
}