use crate::check::Check;
use crate::project::Project;
use crate::runner::HelpOutput;
use crate::types::{CheckGroup, CheckLayer, CheckResult, CheckStatus, Confidence};
const PAGER_SIGNALS: &[&str] = &["less", "more", "$PAGER", "--pager", "pager", "PAGER"];
pub struct NoPagerBehavioralCheck;
impl Check for NoPagerBehavioralCheck {
fn id(&self) -> &str {
"p6-no-pager-behavioral"
}
fn group(&self) -> CheckGroup {
CheckGroup::P6
}
fn layer(&self) -> CheckLayer {
CheckLayer::Behavioral
}
fn covers(&self) -> &'static [&'static str] {
&["p6-must-no-pager"]
}
fn applicable(&self, project: &Project) -> bool {
project.runner.is_some()
}
fn run(&self, project: &Project) -> anyhow::Result<CheckResult> {
let status = match project.help_output() {
None => CheckStatus::Skip("could not probe --help".into()),
Some(help) => check_no_pager(help),
};
Ok(CheckResult {
id: self.id().to_string(),
label: "Pager-using CLI ships --no-pager escape hatch".into(),
group: self.group(),
layer: self.layer(),
status,
confidence: Confidence::Medium,
})
}
}
fn check_no_pager(help: &HelpOutput) -> CheckStatus {
let has_no_pager_flag = help.flags().iter().any(|f| f.matches("--no-pager"));
if has_no_pager_flag {
return CheckStatus::Pass;
}
let raw = help.raw();
let mentions_pager = PAGER_SIGNALS.iter().any(|sig| raw.contains(sig));
if !mentions_pager {
CheckStatus::Skip("no pager signal (less/more/$PAGER/--pager) in --help".into())
} else {
CheckStatus::Warn(
"pager referenced in --help but no --no-pager escape hatch advertised".into(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
const HELP_WITH_NO_PAGER: &str = r#"Usage: tool [OPTIONS]
Options:
--no-pager Disable paged output.
--pager Use custom pager.
-h, --help Show help.
"#;
const HELP_PAGER_WITHOUT_ESCAPE: &str = r#"Usage: tool [OPTIONS]
Long output is piped through less by default. Set $PAGER to override.
Options:
-h, --help Show help.
"#;
const HELP_NO_PAGER_MENTION: &str = r#"Usage: tool [OPTIONS]
Options:
-q, --quiet Suppress output.
-h, --help Show help.
"#;
const HELP_NON_ENGLISH: &str = r#"用法: outil [选项]
选项:
-h, --help 显示帮助
"#;
#[test]
fn happy_path_no_pager_flag_present() {
let help = HelpOutput::from_raw(HELP_WITH_NO_PAGER);
assert_eq!(check_no_pager(&help), CheckStatus::Pass);
}
#[test]
fn skip_when_no_pager_mention_at_all() {
let help = HelpOutput::from_raw(HELP_NO_PAGER_MENTION);
let status = check_no_pager(&help);
assert!(matches!(status, CheckStatus::Skip(_)));
}
#[test]
fn warn_when_pager_mentioned_without_escape() {
let help = HelpOutput::from_raw(HELP_PAGER_WITHOUT_ESCAPE);
let status = check_no_pager(&help);
match status {
CheckStatus::Warn(msg) => {
assert!(msg.contains("--no-pager"));
}
other => panic!("expected Warn, got {other:?}"),
}
}
#[test]
fn non_english_help_skipped() {
let help = HelpOutput::from_raw(HELP_NON_ENGLISH);
let status = check_no_pager(&help);
assert!(matches!(status, CheckStatus::Skip(_)));
}
#[test]
fn detects_no_pager_with_mixed_casing() {
let help = HelpOutput::from_raw(" --no-pager Disable paging.\n");
assert_eq!(check_no_pager(&help), CheckStatus::Pass);
}
}