obadh_engine 0.5.0

A linguistically accurate Roman to Bengali transliteration engine
Documentation
use obadh_engine::{
    AutocorrectConfig, AutocorrectEngine, CorrectionRequest, CorrectionSource, Lexicon,
    LexiconEntry, ObadhEngine,
};

#[test]
fn autocorrect_preserves_exact_obadh_output() {
    let obadh = ObadhEngine::new();
    let autocorrect = AutocorrectEngine::from_entries([LexiconEntry::new("বিজ্ঞান", 10_000)]);
    let request = obadh.autocorrect_request("biggan");

    let decision = autocorrect.decide(request.clone());

    assert_eq!(request.current, "বিজ্ঞান");
    assert_eq!(request.roman_input.as_deref(), Some("biggan"));
    assert_eq!(request.obadh_output.as_deref(), Some("বিজ্ঞান"));
    assert_eq!(decision.replacement, None);
    assert_eq!(decision.candidates[0].source, CorrectionSource::NoChange);
}

#[test]
fn autocorrect_can_fix_simple_bangla_vowel_typos() {
    let autocorrect = AutocorrectEngine::from_entries([
        LexiconEntry::new("আমি", 50_000),
        LexiconEntry::new("আম", 500),
    ]);

    let decision = autocorrect.decide(CorrectionRequest::new("আমী"));

    assert_eq!(
        decision
            .replacement
            .as_ref()
            .map(|candidate| candidate.text.as_str()),
        Some("আমি")
    );
}

#[test]
fn autocorrect_uses_suggestions_when_margin_is_low() {
    let autocorrect = AutocorrectEngine::with_config(
        Lexicon::new([LexiconEntry::new("আমি", 1)]),
        AutocorrectConfig {
            autocorrect_margin: 1_000,
            ..AutocorrectConfig::default()
        },
    );

    let decision = autocorrect.decide(CorrectionRequest::new("আমী"));

    assert!(decision
        .candidates
        .iter()
        .any(|candidate| candidate.text == "আমি"));
    assert_eq!(decision.replacement, None);
}

#[test]
fn autocorrect_candidate_list_is_bounded() {
    let autocorrect = AutocorrectEngine::with_config(
        Lexicon::new([
            LexiconEntry::new("আমি", 10),
            LexiconEntry::new("আম", 9),
            LexiconEntry::new("আমার", 8),
            LexiconEntry::new("আমরা", 7),
        ]),
        AutocorrectConfig {
            max_candidates: 2,
            max_edit_cost: 8,
            max_skeleton_candidates: 0,
            ..AutocorrectConfig::default()
        },
    );

    let decision = autocorrect.decide(CorrectionRequest::new("আমী"));

    assert_eq!(decision.candidates.len(), 2);
    assert_eq!(decision.candidates[0].text, "আমি");
    assert!(decision
        .candidates
        .iter()
        .all(|candidate| candidate.source == CorrectionSource::LexiconEdit));
}

#[test]
fn autocorrect_features_mark_obadh_baseline_candidate() {
    let autocorrect = AutocorrectEngine::from_entries([LexiconEntry::new("আমি", 50_000)]);

    let decision = autocorrect.decide(CorrectionRequest::new("আমী").with_obadh_output("আমী"));
    let keep = decision
        .candidates
        .iter()
        .find(|candidate| candidate.source == CorrectionSource::NoChange)
        .expect("no-change candidate should be present");
    let correction = decision
        .candidates
        .iter()
        .find(|candidate| candidate.text == "আমি")
        .expect("lexicon correction should be present");

    assert!(keep.features.obadh_baseline);
    assert!(!correction.features.obadh_baseline);
    assert!(!correction.features.input_known);
    assert!(correction.features.candidate_known);
    assert_eq!(correction.features.as_i16_array()[0], 1);
}

#[test]
fn autocorrect_suggests_prefix_completions_from_lexicon_frequency() {
    let autocorrect = AutocorrectEngine::with_config(
        Lexicon::new([
            LexiconEntry::new("কেমন", 225),
            LexiconEntry::new("কেমনি", 11),
            LexiconEntry::new("কেমনে", 10),
            LexiconEntry::new("যেমন", 247),
        ]),
        AutocorrectConfig {
            max_candidates: 8,
            max_prefix_candidates: 4,
            max_skeleton_candidates: 0,
            max_edit_cost: 0,
            ..AutocorrectConfig::default()
        },
    );

    let decision = autocorrect.decide(CorrectionRequest::new("কেম"));
    let completions = decision
        .candidates
        .iter()
        .filter(|candidate| candidate.source == CorrectionSource::PrefixCompletion)
        .map(|candidate| candidate.text.as_str())
        .collect::<Vec<_>>();

    assert_eq!(completions, vec!["কেমন", "কেমনি", "কেমনে"]);
    assert_eq!(decision.replacement, None);
    let kemon = decision
        .candidates
        .iter()
        .find(|candidate| candidate.text == "কেমন")
        .expect("prefix completion should include কেমন");
    assert_eq!(kemon.features.source_id, 4);
    assert_eq!(kemon.edit_cost.0, 0);
}