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 ordinals_map(s: &str) -> Option<i64> {
    match s {
        "első" => Some(1),
        "második" => Some(2),
        "harmadik" => Some(3),
        "negyedik" => Some(4),
        "ötödik" => Some(5),
        "hatodik" => Some(6),
        "hetedik" => Some(7),
        "nyolcadik" => Some(8),
        "kilencedik" => Some(9),
        "tizedik" => Some(10),
        "huszadik" => Some(20),
        "harmincadik" => Some(30),
        "negyvenedik" => Some(40),
        "ötvenedik" => Some(50),
        "hatvanadik" => Some(60),
        "hetvenedik" => Some(70),
        "nyolcvanadik" => Some(80),
        "kilencvenedik" => Some(90),
        _ => None,
    }
}

fn ordinals_map2(s: &str) -> Option<i64> {
    match s {
        "egyedik" => Some(1),
        "kettedik" => Some(2),
        "harmadik" => Some(3),
        "negyedik" => Some(4),
        "ötödik" => Some(5),
        "hatodik" => Some(6),
        "hetedik" => Some(7),
        "nyolcadik" => Some(8),
        "kilencedik" => Some(9),
        _ => None,
    }
}

fn cardinals_map(s: &str) -> Option<i64> {
    match s {
        "tizen" => Some(10),
        "huszon" => Some(20),
        "harminc" => Some(30),
        "negyven" => Some(40),
        "ötven" => Some(50),
        "hatvan" => Some(60),
        "hetven" => Some(70),
        "nyolcvan" => Some(80),
        "kilencven" => Some(90),
        _ => None,
    }
}

pub fn rules() -> Vec<Rule> {
    vec![
        Rule {
            name: "ordinals (first..twentieth,thirtieth,...)".to_string(),
            pattern: vec![regex("(első|második|harmadik|negyedik|ötödik|hatodik|hetedik|nyolcadik|kilencedik|tizedik|huszadik|harmincadik|negyvenedik|ötvenedik|hatvanadik|hetvenedik|nyolcvanadik|kilencvenedik)")],
            production: Box::new(|nodes| {
                let text = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(ordinals_map(text)?)))
            }),
        },
        Rule {
            name: "ordinals (composite, e.g., eighty-seven)".to_string(),
            pattern: vec![regex("(tizen|huszon|harminc|negyven|ötven|hatvan|hetven|nyolcvan|kilencven)\\-?(egyedik|kettedik|harmadik|negyedik|ötödik|hatodik|hetedik|nyolcadik|kilencedik)")],
            production: Box::new(|nodes| {
                let tens = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?,
                    _ => return None,
                };
                let units = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(2)?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(
                    cardinals_map(tens)?.checked_add(ordinals_map2(units)?)?,
                )))
            }),
        },
        Rule {
            name: "ordinal (digits)".to_string(),
            pattern: vec![regex("0*(\\d+)\\.")],
            production: Box::new(|nodes| {
                let value: i64 = match &nodes[0].token_data {
                    TokenData::RegexMatch(m) => m.group(1)?.parse().ok()?,
                    _ => return None,
                };
                Some(TokenData::Ordinal(OrdinalData::new(value)))
            }),
        },
    ]
}