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 lookup_ordinal_word(s: &str) -> Option<i64> {
    match s.to_lowercase().as_str() {
        "erste" => Some(1),
        "zweite" => Some(2),
        "dritte" => Some(3),
        "vierte" => Some(4),
        "fünfte" => Some(5),
        "sechste" => Some(6),
        "siebte" => Some(7),
        "achte" => Some(8),
        "neunte" => Some(9),
        "zehnte" => Some(10),
        "elfte" => Some(11),
        "zwölfte" => Some(12),
        "dreizente" => Some(13),
        "vierzehnte" => Some(14),
        "fünfzehnte" => Some(15),
        "sechzente" => Some(16),
        "siebzehnte" => Some(17),
        "achtzehnte" => Some(18),
        "neunzehnte" => Some(19),
        "zwanzigste" => Some(20),
        "einundzwanzigste" => Some(21),
        "zweiundzwanzigste" => Some(22),
        "dreiundzwanzigste" => Some(23),
        "vierundzwanzigste" => Some(24),
        "fünfundzwanzigste" => Some(25),
        "sechsundzwanzigste" => Some(26),
        "siebenundzwanzigste" => Some(27),
        "achtundzwanzigste" => Some(28),
        "neunundzwanzigste" => Some(29),
        "dreissigste" | "dreißigste" => Some(30),
        "einunddreissigste" | "einunddreißigste" => Some(31),
        _ => None,
    }
}

pub fn rules() -> Vec<Rule> {
    vec![
        Rule {
            name: "ordinal (1..31)".to_string(),
            pattern: vec![regex(
                "(erste|zweite|dritte|vierte|fünfte|sechste|siebte|achte|neunte|zehnte|elfte|zwölfte|dreizente|vierzehnte|fünfzehnte|sechzente|siebzehnte|achtzehnte|neunzehnte|zwanzigste|einundzwanzigste|zweiundzwanzigste|dreiundzwanzigste|vierundzwanzigste|fünfundzwanzigste|sechsundzwanzigste|siebenundzwanzigste|achtundzwanzigste|neunundzwanzigste|dreissigste|dreißigste|einunddreissigste|einunddreißigste)[rsn]?",
            )],
            production: Box::new(|nodes| {
                let mut s = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?.to_lowercase(),
                    _ => return None,
                };
                if s.ends_with('r') || s.ends_with('s') || s.ends_with('n') {
                    s.pop();
                }
                Some(TokenData::Ordinal(OrdinalData::new(lookup_ordinal_word(&s)?)))
            }),
        },
        Rule {
            name: "ordinal (digits)".to_string(),
            pattern: vec![regex("(\\d+)(\\.| ?(te(n|r|s)?)|(ste(n|r|s)?))")],
            production: Box::new(|nodes| {
                let s = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                let v: i64 = s.parse().ok()?;
                Some(TokenData::Ordinal(OrdinalData::new(v)))
            }),
        },
    ]
}