readsight 1.0.0

Multilingual readability library — 86 languages, 17 formulas, TeX-based syllable counting via the Frank M. Liang algorithm.
Documentation
//! Formula registry and factory.

use std::collections::HashMap;

use crate::error::{Error, Result};
use crate::language::Language;
use crate::text::TextStatistics;

use super::impls::*;
use super::{Formula, FormulaResult};

/// An ordered registry of formulas keyed by name.
pub struct FormulaRegistry {
    formulas: Vec<Box<dyn Formula>>,
    index: HashMap<String, usize>,
}

impl FormulaRegistry {
    /// Create an empty registry.
    pub fn new() -> Self {
        FormulaRegistry {
            formulas: Vec::new(),
            index: HashMap::new(),
        }
    }

    /// Register a formula (last registration for a name wins).
    pub fn register(&mut self, formula: Box<dyn Formula>) {
        let name = formula.name().to_string();
        if let Some(&i) = self.index.get(&name) {
            self.formulas[i] = formula;
        } else {
            self.index.insert(name, self.formulas.len());
            self.formulas.push(formula);
        }
    }

    /// Whether a formula with `name` exists.
    pub fn has(&self, name: &str) -> bool {
        self.index.contains_key(name)
    }

    /// Borrow a formula by name.
    pub fn get(&self, name: &str) -> Option<&dyn Formula> {
        self.index.get(name).map(|&i| self.formulas[i].as_ref())
    }

    /// Names in registration order.
    pub fn list_names(&self) -> Vec<String> {
        self.formulas.iter().map(|f| f.name().to_string()).collect()
    }

    /// Names supported for `language`, in registration order.
    pub fn list_for_language(&self, language: &Language) -> Vec<String> {
        self.formulas
            .iter()
            .filter(|f| is_supported(f.as_ref(), language))
            .map(|f| f.name().to_string())
            .collect()
    }

    /// Evaluate `name` for `language` over `stats`.
    pub fn calculate(
        &self,
        name: &str,
        language: &Language,
        stats: &TextStatistics,
    ) -> Result<FormulaResult> {
        match self.get(name) {
            Some(f) if is_supported(f, language) => Ok(f.calculate(stats, language)),
            _ => Err(Error::UnsupportedFormula {
                formula: name.to_string(),
                language: language.code.clone(),
            }),
        }
    }
}

impl Default for FormulaRegistry {
    fn default() -> Self {
        Self::new()
    }
}

fn is_supported(formula: &dyn Formula, language: &Language) -> bool {
    let langs = formula.supported_languages();
    langs == ["*"] || langs.contains(&language.code.as_str())
}

/// Build a registry with all 17 formulas registered in the canonical order.
pub fn create() -> FormulaRegistry {
    let mut registry = FormulaRegistry::new();
    registry.register(Box::new(AutomatedReadabilityIndex));
    registry.register(Box::new(ColemanLiau));
    registry.register(Box::new(Crawford));
    registry.register(Box::new(DaleChall));
    registry.register(Box::new(FernandezHuerta));
    registry.register(Box::new(FleschKincaidGradeLevel));
    registry.register(Box::new(FleschReadingEase));
    registry.register(Box::new(FogPL));
    registry.register(Box::new(Gulpease));
    registry.register(Box::new(GunningFog));
    registry.register(Box::new(GutierrezPolini));
    registry.register(Box::new(Lix));
    registry.register(Box::new(Osman));
    registry.register(Box::new(SmogIndex));
    registry.register(Box::new(Spache));
    registry.register(Box::new(SzigrisztPazos));
    registry.register(Box::new(WienerSachtextformel));
    registry
}