Skip to main content

trueno_ptx_debug/
cli.rs

1//! Declarative CLI definition for the `aprender-ptx-debug` binary.
2//!
3//! The parser lives in the library rather than in `src/bin/main.rs` so that
4//! integration tests can exercise it directly, matching the house pattern used
5//! by the other CLI crates in this workspace.
6//!
7//! Hand-rolled `match args[1]` dispatch is banned here: unknown flags fall
8//! through catch-all arms, a valued flag given without a value gets discarded,
9//! and an unparseable value degrades into a default instead of an error. clap
10//! derive makes each of those a hard parse failure.
11
12use clap::error::ErrorKind;
13use clap::{Args, CommandFactory, Parser, Subcommand};
14
15/// Trailing help text, preserved verbatim from the original usage banner.
16const AFTER_HELP: &str = "EXIT CODES:
17    0 - Analysis passed (score >= 90)
18    1 - Analysis passed with warnings (score 70-89)
19    2 - Analysis failed (score < 70)
20    3 - Critical bugs detected
21    10 - Parse error
22    11 - I/O error
23
24EXAMPLES:
25    aprender-ptx-debug analyze kernel.ptx --falsify
26    aprender-ptx-debug analyze kernel.ptx --min-score 90 --html report.html
27    aprender-ptx-debug gen-fkr kernel.ptx -o tests/kernel_fkr.rs";
28
29/// Top-level command line for `aprender-ptx-debug`.
30#[derive(Debug, Parser)]
31#[command(
32    name = "aprender-ptx-debug",
33    about = "Pure Rust PTX debugging and static analysis tool",
34    version,
35    subcommand_required = true,
36    arg_required_else_help = true,
37    after_help = AFTER_HELP
38)]
39pub struct Cli {
40    /// Subcommand to execute.
41    #[command(subcommand)]
42    pub command: Command,
43}
44
45/// Available subcommands.
46#[derive(Debug, Subcommand)]
47pub enum Command {
48    /// Analyze PTX file for bugs and issues
49    Analyze(AnalyzeArgs),
50
51    /// Generate FKR tests for jugar-probar
52    #[command(name = "gen-fkr")]
53    GenFkr(GenFkrArgs),
54
55    /// Show version information
56    Version,
57}
58
59/// Arguments for the `analyze` subcommand.
60#[derive(Debug, Args)]
61pub struct AnalyzeArgs {
62    /// PTX file to analyze
63    #[arg(value_name = "FILE")]
64    pub file: String,
65
66    /// Run full 100-point falsification framework.
67    ///
68    /// The framework is always evaluated by `analyze`, so this flag is accepted
69    /// for backwards compatibility and does not currently change the output.
70    #[arg(long)]
71    pub falsify: bool,
72
73    /// Fail if score < N
74    #[arg(long = "min-score", value_name = "N", default_value_t = 70.0)]
75    pub min_score: f64,
76
77    /// Write HTML report to file
78    #[arg(long, value_name = "FILE")]
79    pub html: Option<String>,
80
81    /// Output JSON format
82    #[arg(long)]
83    pub json: bool,
84}
85
86/// Arguments for the `gen-fkr` subcommand.
87#[derive(Debug, Args)]
88pub struct GenFkrArgs {
89    /// PTX file to generate tests from
90    #[arg(value_name = "FILE")]
91    pub file: String,
92
93    /// Output file (default: stdout)
94    #[arg(short = 'o', value_name = "FILE")]
95    pub output: Option<String>,
96}
97
98/// Render the version string used by both `--version` and the `version`
99/// subcommand, so the two surfaces cannot drift apart.
100#[must_use]
101pub fn version_string() -> String {
102    Cli::command().render_version()
103}
104
105/// Map a clap parse failure onto the process exit code.
106///
107/// `--help` and `--version` are reported by clap as errors but are successful
108/// invocations. Every other parse failure exits 1, preserving the exit status
109/// the hand-rolled parser used for an unknown command, a missing argument, or a
110/// bad option value.
111#[must_use]
112pub fn exit_code_for_parse_error(err: &clap::Error) -> i32 {
113    match err.kind() {
114        ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => 0,
115        _ => 1,
116    }
117}