use low_expectations::ExpectationSuite;
use prosesmasher_domain_types::{CheckConfig, Document, Locale};
use serde_json::json;
use crate::check::Check;
#[derive(Debug)]
pub struct FleschKincaidCheck;
impl Check for FleschKincaidCheck {
fn id(&self) -> &'static str {
"flesch-kincaid"
}
fn label(&self) -> &'static str {
"Flesch-Kincaid Reading Ease"
}
fn supported_locales(&self) -> Option<&'static [Locale]> {
None
}
fn run(&self, doc: &Document, config: &CheckConfig, suite: &mut ExpectationSuite) {
if !config.quality.readability.enabled {
return;
}
let Some(min) = config.quality.readability.flesch_kincaid_min else {
return;
};
let total_words = doc.metadata.total_words;
let total_sentences = doc.metadata.total_sentences;
let total_syllables = doc.metadata.total_syllables;
if total_sentences == 0 || total_words == 0 {
return;
}
let words_f = f64::from(u32::try_from(total_words).unwrap_or(u32::MAX));
let sentences_f = f64::from(u32::try_from(total_sentences).unwrap_or(u32::MAX));
let syllables_f = f64::from(u32::try_from(total_syllables).unwrap_or(u32::MAX));
let score = 1.015f64.mul_add(
-(words_f / sentences_f),
84.6f64.mul_add(-(syllables_f / words_f), 206.835),
);
let score_100 = f64_to_i64_x100(score);
let min_100 = f64_to_i64_x100(min);
let _result = suite
.record_custom_values(
"flesch-kincaid",
score_100 >= min_100,
json!({
"minimum_score_x100": min_100,
"formula": "206.835 - 1.015 × (words/sentences) - 84.6 × (syllables/words)",
}),
json!({
"score_x100": score_100,
"score": score,
"total_words": total_words,
"total_sentences": total_sentences,
"total_syllables": total_syllables,
}),
&[json!({
"score_x100": score_100,
"score": score,
"total_words": total_words,
"total_sentences": total_sentences,
"total_syllables": total_syllables,
"minimum_score_x100": min_100,
})],
)
.label("Flesch-Kincaid Reading Ease")
.checking("reading ease score (×100)");
}
}
fn f64_to_i64_x100(value: f64) -> i64 {
let rounded = (value * 100.0).round();
let clamped = rounded.clamp(f64::from(i32::MIN), f64::from(i32::MAX));
#[allow(clippy::cast_possible_truncation, clippy::as_conversions)]
let narrow = clamped as i32;
i64::from(narrow)
}
#[cfg(test)]
#[path = "flesch_kincaid_tests.rs"]
mod tests;