readsight 1.0.0

Multilingual readability library — 86 languages, 17 formulas, TeX-based syllable counting via the Frank M. Liang algorithm.
Documentation
//! Wiener Sachtextformel (German), with 4 variants.

use crate::error::{Error, Result};
use crate::formula::{inputs, round_to, Formula, FormulaResult};
use crate::language::Language;
use crate::text::TextStatistics;

/// Wiener Sachtextformel.
pub struct WienerSachtextformel;

impl WienerSachtextformel {
    fn long_word_pct(stats: &TextStatistics) -> f64 {
        if stats.word_count > 0 {
            (stats.long_word_count as f64 / stats.word_count as f64) * 100.0
        } else {
            0.0
        }
    }

    fn one_syllable_pct(stats: &TextStatistics) -> f64 {
        let one = stats.syllable_histogram.get(&1).copied().unwrap_or(0);
        if stats.word_count > 0 {
            (one as f64 / stats.word_count as f64) * 100.0
        } else {
            0.0
        }
    }

    /// Evaluate a specific variant (1..=4).
    pub fn calculate_variant(
        &self,
        stats: &TextStatistics,
        language: &Language,
        variant: i32,
    ) -> Result<FormulaResult> {
        let w = stats.word_count.max(1) as f64;
        let ms = (stats.polysyllable_count as f64 / w) * 100.0;
        let sl = stats.average_words_per_sentence;
        let iw = Self::long_word_pct(stats);
        let mut es = 0.0;

        let score = match variant {
            1 => {
                es = Self::one_syllable_pct(stats);
                0.1935 * ms + 0.1672 * sl + 0.1297 * iw - 0.0327 * es - 0.875
            }
            2 => 0.2007 * ms + 0.1682 * sl + 0.1373 * iw - 2.779,
            3 => 0.2963 * ms + 0.1905 * sl - 1.1144,
            4 => 0.2744 * ms + 0.2656 * sl - 1.693,
            other => return Err(Error::InvalidVariant(other)),
        };

        let rounded = round_to(score, 1);
        let grade_level = score.clamp(4.0, 15.0);

        Ok(FormulaResult {
            formula_name: format!("{}_{}", self.name(), variant),
            language_code: language.code.clone(),
            score: rounded,
            grade_level: Some(grade_level),
            interpretation: interpret_wstf(rounded).to_string(),
            inputs: inputs(&[
                ("ms", ms),
                ("sl", sl),
                ("iw", iw),
                ("es", es),
                ("variant", variant as f64),
            ]),
        })
    }
}

impl Formula for WienerSachtextformel {
    fn name(&self) -> &'static str {
        "wiener_sachtextformel"
    }
    fn description(&self) -> &'static str {
        "Wiener Sachtextformel - German readability formula with 4 variants. Returns school grade level."
    }
    fn supported_languages(&self) -> &'static [&'static str] {
        &["de-1996", "de-1901", "de-ch-1901"]
    }
    fn calculate(&self, stats: &TextStatistics, language: &Language) -> FormulaResult {
        self.calculate_variant(stats, language, 1)
            .expect("variant 1 is always valid")
    }
}

fn interpret_wstf(score: f64) -> &'static str {
    if score < 5.0 {
        "Very Easy"
    } else if score < 7.0 {
        "Easy"
    } else if score < 9.0 {
        "Standard"
    } else if score < 11.0 {
        "Fairly Hard"
    } else if score < 13.0 {
        "Hard"
    } else {
        "Very Hard"
    }
}