pub mod acronyms;
pub mod cliches;
pub mod complex_paragraphs;
pub mod conjunction_starts;
pub mod consistency;
pub mod diction;
pub mod echoes;
pub mod jargon;
pub mod overused;
pub mod pacing;
pub mod repeated;
pub mod reports;
pub mod sensory;
pub mod sentence_length;
pub mod sticky;
pub mod style;
pub mod transitions;
use std::collections::HashSet;
pub use reports::FullAnalysisReport;
use crate::config::Dialect;
use crate::error::{AnalysisError, AnalysisResult};
use crate::grammar;
use crate::markdown;
use crate::readability;
use crate::text;
pub const ALL_CHECKS: &[&str] = &[
"readability",
"grammar",
"sticky",
"pacing",
"sentence_length",
"transitions",
"overused",
"repeated",
"echoes",
"sensory",
"diction",
"cliches",
"consistency",
"acronyms",
"jargon",
"complex_paragraphs",
"conjunction_starts",
"style",
];
#[tracing::instrument(skip(input), fields(text_len = input.len(), strip_md))]
pub fn run_full_analysis(
input: &str,
strip_md: bool,
checks: Option<&[String]>,
max_grade: Option<f64>,
passive_max: Option<f64>,
dialect: Option<Dialect>,
) -> AnalysisResult<FullAnalysisReport> {
let prose = if strip_md {
markdown::strip_to_prose(input)
} else {
input.to_string()
};
if prose.trim().is_empty() {
return Err(AnalysisError::EmptyInput);
}
let enabled: HashSet<&str> = checks.map_or_else(
|| ALL_CHECKS.iter().copied().collect(),
|list| list.iter().map(String::as_str).collect(),
);
let sentences = text::split_sentences(&prose);
let words = text::extract_words(&prose);
let paragraphs = text::split_paragraphs(&prose);
let readability_report = if enabled.contains("readability") {
readability::check_readability(&prose, false, max_grade).ok()
} else {
None
};
let grammar_report = if enabled.contains("grammar") {
grammar::check_grammar_full(&prose, false, passive_max).ok()
} else {
None
};
let passive_count = grammar_report.as_ref().map_or(0, |r| r.passive_count);
let sticky_report = if enabled.contains("sticky") {
Some(sticky::analyze_sticky_sentences(&sentences, &words))
} else {
None
};
let pacing_report = if enabled.contains("pacing") {
Some(pacing::analyze_pacing(&sentences))
} else {
None
};
let sentence_length_report = if enabled.contains("sentence_length") {
Some(sentence_length::analyze_sentence_length(&sentences))
} else {
None
};
let transitions_report = if enabled.contains("transitions") {
Some(transitions::analyze_transitions(&sentences))
} else {
None
};
let overused_report = if enabled.contains("overused") {
Some(overused::analyze_overused_words(&words))
} else {
None
};
let repeated_report = if enabled.contains("repeated") {
Some(repeated::analyze_repeated_phrases(&words))
} else {
None
};
let echoes_report = if enabled.contains("echoes") {
Some(echoes::analyze_echoes(¶graphs))
} else {
None
};
let sensory_report = if enabled.contains("sensory") {
Some(sensory::analyze_sensory_words(&words))
} else {
None
};
let diction_report = if enabled.contains("diction") {
Some(diction::analyze_diction(&prose, &words))
} else {
None
};
let cliches_report = if enabled.contains("cliches") {
Some(cliches::analyze_cliches(&prose))
} else {
None
};
let consistency_report = if enabled.contains("consistency") {
Some(consistency::analyze_consistency(&prose, dialect))
} else {
None
};
let acronyms_report = if enabled.contains("acronyms") {
Some(acronyms::analyze_acronyms(&prose))
} else {
None
};
let jargon_report = if enabled.contains("jargon") {
Some(jargon::analyze_business_jargon(&prose, &words))
} else {
None
};
let complex_report = if enabled.contains("complex_paragraphs") {
Some(complex_paragraphs::analyze_complex_paragraphs(¶graphs))
} else {
None
};
let conjunction_report = if enabled.contains("conjunction_starts") {
Some(conjunction_starts::analyze_conjunction_starts(&sentences))
} else {
None
};
let style_report = if enabled.contains("style") {
let sticky_for_score = sticky_report
.as_ref()
.cloned()
.unwrap_or_else(|| sticky::analyze_sticky_sentences(&sentences, &words));
let diction_for_score = diction_report
.as_ref()
.cloned()
.unwrap_or_else(|| diction::analyze_diction(&prose, &words));
Some(style::analyze_style(
&prose,
&words,
passive_count,
&sticky_for_score,
&diction_for_score,
))
} else {
None
};
Ok(FullAnalysisReport {
readability: readability_report,
grammar: grammar_report,
sticky_sentences: sticky_report,
pacing: pacing_report,
sentence_length: sentence_length_report,
transitions: transitions_report,
overused_words: overused_report,
repeated_phrases: repeated_report,
echoes: echoes_report,
sensory: sensory_report,
diction: diction_report,
cliches: cliches_report,
consistency: consistency_report,
acronyms: acronyms_report,
jargon: jargon_report,
complex_paragraphs: complex_report,
conjunction_starts: conjunction_report,
style: style_report,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn full_analysis_runs() {
let text = "The cat sat on the mat. The dog ran fast. However, the bird flew away.";
let report = run_full_analysis(text, false, None, None, None, None).unwrap();
assert!(report.readability.is_some());
assert!(report.grammar.is_some());
assert!(report.sticky_sentences.is_some());
assert!(report.pacing.is_some());
assert!(report.style.is_some());
}
#[test]
fn selective_checks() {
let text = "The cat sat on the mat. The dog ran fast.";
let checks = vec!["readability".to_string(), "pacing".to_string()];
let report = run_full_analysis(text, false, Some(&checks), None, None, None).unwrap();
assert!(report.readability.is_some());
assert!(report.pacing.is_some());
assert!(report.grammar.is_none());
assert!(report.style.is_none());
}
#[test]
fn empty_input_errors() {
let result = run_full_analysis("", false, None, None, None, None);
assert!(result.is_err());
}
#[test]
fn markdown_stripping_works() {
let md = "# Title\n\nThe cat sat on the mat.\n\n```rust\nlet x = 1;\n```";
let report = run_full_analysis(md, true, None, None, None, None).unwrap();
assert!(report.readability.is_some());
}
#[test]
fn full_analysis_with_dialect() {
let text = "The colour of the centre was organised well.";
let report = run_full_analysis(
text,
false,
Some(&["consistency".to_string()]),
None,
None,
Some(Dialect::EnUs),
)
.unwrap();
let c = report.consistency.expect("consistency report should exist");
assert_eq!(c.dialect.as_deref(), Some("en-us"));
assert!(c.total_issues > 0, "should flag UK spellings for en-us");
}
}