duckling 0.4.0

A Rust port of Facebook's Duckling library for parsing natural language into structured data
Documentation
use crate::pattern::regex;
use crate::types::{Rule, TokenData};

use super::OrdinalData;

fn normalize(s: &str) -> String {
    s.to_lowercase()
        .replace('á', "a")
        .replace('é', "e")
        .replace('í', "i")
        .replace('ó', "o")
        .replace('ú', "u")
}

fn lookup_ordinal(s: &str) -> Option<i64> {
    match normalize(s).as_str() {
        "t-aonu" | "aonu" | "chead" => Some(1),
        "dara" => Some(2),
        "triu" => Some(3),
        "ceathru" => Some(4),
        "cuigiu" => Some(5),
        "seu" => Some(6),
        "seachtu" => Some(7),
        "t-ochtu" | "ochtu" => Some(8),
        "naou" => Some(9),
        "deichiu" => Some(10),
        "fichiu" => Some(20),
        "triochadu" => Some(30),
        "daicheadu" => Some(40),
        "caogadu" => Some(50),
        "seascadu" => Some(60),
        "seachtodu" => Some(70),
        "ochtodu" | "t-ochtodu" => Some(80),
        "nochadu" => Some(90),
        "ceadu" => Some(100),
        "miliu" => Some(1000),
        "milliunu" => Some(1_000_000),
        _ => None,
    }
}

pub fn rules() -> Vec<Rule> {
    vec![
        Rule {
            name: "ordinal (digits)".to_string(),
            pattern: vec![regex("0*(\\d+) ?(adh|a|d|ú|u)")],
            production: Box::new(|nodes| {
                let v: i64 = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?.parse().ok()?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(v)))
            }),
        },
        Rule {
            name: "ordinals (chéad, dara, etc.)".to_string(),
            pattern: vec![regex("(ch(é|e)ad|aon(ú|u)|t-aon(ú|u)|dara|tr(í|i)(ú|u)|ceathr(ú|u)|c(ú|u)igi(ú|u)|s(é|e)(ú|u)|seacht(ú|u)|ocht(ú|u)|t-ocht(ú|u)|nao(ú|u)|deichi(ú|u)|fichi(ú|u)|tr(í|i)ochad(ú|u)|daichead(ú|u)|caogad(ú|u)|seascad(ú|u)|seacht(ó|o)d(ú|u)|ocht(ó|o)d(ú|u)|t-ocht(ó|o)d(ú|u)|n(ó|o)chad(ú|u)|c(é|e)ad(ú|u)|mili(ú|u)|milli(ú|u)n(ú|u))")],
            production: Box::new(|nodes| {
                let s = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                let v = lookup_ordinal(s)?;
                Some(TokenData::Ordinal(OrdinalData::new(v)))
            }),
        },
    ]
}