use std::{collections::HashMap, sync::Mutex};
use crate::{
runner::aligner::{
aligner::Aligner,
key::{AlignerKey, AlignmentFallback},
},
types::Lang,
};
pub enum AlignmentLookup<'a> {
Hit {
matched: AlignerKey,
aligner: &'a Mutex<Aligner>,
},
AnyFallback {
aligner: &'a Mutex<Aligner>,
},
Miss {
fallback: AlignmentFallback,
},
}
pub struct AlignmentSet {
aligners: HashMap<AlignerKey, Mutex<Aligner>>,
fallback: AlignmentFallback,
}
impl AlignmentSet {
pub(super) const fn from_parts(
aligners: HashMap<AlignerKey, Mutex<Aligner>>,
fallback: AlignmentFallback,
) -> Self {
Self { aligners, fallback }
}
pub const fn fallback(&self) -> AlignmentFallback {
self.fallback
}
pub fn len(&self) -> usize {
self.aligners.len()
}
pub fn is_empty(&self) -> bool {
self.aligners.is_empty()
}
pub fn detect_oov(
&self,
text: &str,
language: &Lang,
) -> Result<Vec<crate::core::OovEvent>, crate::types::WorkFailure> {
let aligner_mu = match self.lookup(language) {
AlignmentLookup::Hit { aligner, .. } | AlignmentLookup::AnyFallback { aligner } => aligner,
AlignmentLookup::Miss { .. } => return Ok(Vec::new()),
};
let guard = aligner_mu.lock().unwrap_or_else(|p| p.into_inner());
let mut events = guard.detect_oov(text)?;
for ev in &mut events {
ev.set_language(language.clone());
}
Ok(events)
}
pub fn detect_oov_per_run(
&self,
runs: &[crate::align::Run],
) -> Result<Vec<Vec<crate::core::OovEvent>>, crate::types::WorkFailure> {
let mut out = Vec::with_capacity(runs.len());
for run in runs {
out.push(self.detect_oov(run.text(), run.language())?);
}
Ok(out)
}
pub fn lookup<'a>(&'a self, language: &Lang) -> AlignmentLookup<'a> {
let lang_key = AlignerKey::Lang(language.clone());
if let Some(m) = self.aligners.get(&lang_key) {
return AlignmentLookup::Hit {
matched: lang_key,
aligner: m,
};
}
if let Some(m) = self.aligners.get(&AlignerKey::Any) {
return AlignmentLookup::AnyFallback { aligner: m };
}
AlignmentLookup::Miss {
fallback: self.fallback,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::runner::aligner::{normalizer::DynTextNormalizer, normalizers::EnglishNormalizer};
#[test]
fn empty_set_misses_with_default_fallback() {
let set = AlignmentSet::from_parts(HashMap::new(), AlignmentFallback::SkipChunk);
match set.lookup(&Lang::En) {
AlignmentLookup::Miss { fallback } => {
assert_eq!(fallback, AlignmentFallback::SkipChunk);
}
_ => panic!("expected Miss"),
}
}
#[test]
fn empty_set_misses_with_error_fallback() {
let set = AlignmentSet::from_parts(HashMap::new(), AlignmentFallback::Error);
match set.lookup(&Lang::Zh) {
AlignmentLookup::Miss { fallback } => {
assert_eq!(fallback, AlignmentFallback::Error);
}
_ => panic!("expected Miss"),
}
}
#[test]
fn is_empty_reports_correctly() {
let set = AlignmentSet::from_parts(HashMap::new(), AlignmentFallback::SkipChunk);
assert!(set.is_empty());
assert_eq!(set.len(), 0);
}
#[test]
fn normalizer_imports_compile() {
let _: DynTextNormalizer = Box::new(EnglishNormalizer::new());
}
}