///|
/// Analyze `text` once and return the 19-field value object of spec §4.2
/// (PRD-A FR-01..FR-23; spec §1.3.2; design D-5).
///
/// The fields are computed by the 19 public single-metric entries, each under
/// the default shape frozen in the oracle's `results` keys (spec §4.2
/// constraint 3):
///
/// - `char_count` with `ignore_spaces=true`;
/// - `lexicon_count` with `removepunct=true`, `split_contractions=false` and
/// `split_hyphens=false`;
/// - `syllable_count`, `polysyllabcount`, `monosyllabcount`,
/// `flesch_reading_ease`, `flesch_kincaid_grade`, `gunning_fog`,
/// `smog_index` and `dale_chall_readability_score` with the requested
/// `lang`;
/// - `difficult_words` and `difficult_words_list` with
/// `syllable_threshold=2` and `unique=true`;
/// - `long_word_count` with `threshold=6`;
/// - `reading_time` with `14.69` ms per non-whitespace character;
/// - `linsear_write_formula` with `strict_upper=true` and `strict_lower=false`;
/// - `automated_readability_index` and `coleman_liau_index` with no language
/// parameter.
///
/// The record literal lists the 19 fields in the spec §4.2 order (the order of
/// `TextStats`), so the returned value object has a stable shape. Nothing is
/// reimplemented here: the entry only sequences the existing public entries,
/// and no second tokenization path exists (spec §2.1 constraint 2).
///
/// **Failure channel.** `syllable_count` is the first syllable-based metric in
/// field order, so an unsupported language on non-empty text raises there and
/// the call stops before any English substitution (spec §4.5.4). The error is
/// the inner metric's `MetricFailure`, whose text still carries the three
/// required items — metric name, language code and failure reason (A-5.4) —
/// and it propagates unchanged rather than being rewrapped under `analyze`.
/// The empty string keeps that entry's documented early return
/// (`syllable_count("")` is 0 for every language), and the other entries then
/// return their zero shapes, exactly as the single-metric contracts describe.
///
/// The function is pure: no I/O. This entry exposes only `lang`; the variant
/// switches (difficult-word syllable threshold and de-duplication, long-word
/// threshold, reading-time pace, Linsear strictness) remain reachable through
/// the corresponding single-metric entry, which is their contract surface.
pub fn analyze(
text : String,
lang~ : String = "en_US",
) -> TextStats raise TextstatError {
let chars = char_count(text)
let letters = letter_count(text)
let words = lexicon_count(text)
let sentences = sentence_count(text)
let syllables = syllable_count(text, lang~)
let poly = polysyllabcount(text, lang~)
let mono = monosyllabcount(text, lang~)
let difficult = difficult_words(text, lang~)
let difficult_list = difficult_words_list(text, lang~)
let long_words = long_word_count(text)
let seconds = reading_time(text)
let fre = flesch_reading_ease(text, lang~)
let fk = flesch_kincaid_grade(text, lang~)
let fog = gunning_fog(text, lang~)
let smog = smog_index(text, lang~)
let ari = automated_readability_index(text)
let cl = coleman_liau_index(text)
let linsear = linsear_write_formula(
text,
lang~,
strict_lower=false,
strict_upper=true,
)
let dc = dale_chall_readability_score(text, lang~)
{
char_count: chars,
letter_count: letters,
lexicon_count: words,
sentence_count: sentences,
syllable_count: syllables,
polysyllabcount: poly,
monosyllabcount: mono,
difficult_words: difficult,
difficult_words_list: difficult_list,
long_word_count: long_words,
reading_time: seconds,
flesch_reading_ease: fre,
flesch_kincaid_grade: fk,
gunning_fog: fog,
smog_index: smog,
automated_readability_index: ari,
coleman_liau_index: cl,
linsear_write_formula: linsear,
dale_chall_readability_score: dc,
}
}