Skip to main content

heddle_cli_args/cli/cli_args/
command_suggestions.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Near-miss table for Git-shaped verbs that are not Heddle commands.
3//!
4//! Clap already suggests Levenshtein-near names (`statuz` → `status`).
5//! Users who follow the old text vocabulary (`save`) or Git muscle memory
6//! (`add`, `stash`) land too far from the real verb for that heuristic.
7
8/// Suggested replacement for an unrecognized top-level subcommand.
9pub fn suggested_command(unknown: &str) -> Option<&'static str> {
10    match unknown.trim().to_ascii_lowercase().as_str() {
11        "save" | "add" => Some("capture"),
12        "stash" => Some("start"),
13        "presence" => Some("agent presence"),
14        "timeline" => Some("agent timeline"),
15        "collapse" => Some("thread collapse"),
16        "expand" => Some("thread expand"),
17        "oplog" => Some("maintenance oplog recover"),
18        _ => None,
19    }
20}
21
22/// Clap-shaped usage error that names the near-miss.
23pub fn format_unrecognized_suggestion(unknown: &str, suggested: &str) -> String {
24    format!(
25        "error: unrecognized subcommand '{unknown}'\n\n  tip: a similar subcommand exists: '{suggested}'\n\nUsage: heddle [OPTIONS] <COMMAND>\n\nFor more information, try '--help'.\n"
26    )
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn save_and_add_suggest_capture() {
35        assert_eq!(suggested_command("save"), Some("capture"));
36        assert_eq!(suggested_command("SAVE"), Some("capture"));
37        assert_eq!(suggested_command("add"), Some("capture"));
38    }
39
40    #[test]
41    fn stash_suggests_start() {
42        assert_eq!(suggested_command("stash"), Some("start"));
43    }
44
45    #[test]
46    fn moved_commands_suggest_their_nested_paths() {
47        for (old, new) in [
48            ("presence", "agent presence"),
49            ("timeline", "agent timeline"),
50            ("collapse", "thread collapse"),
51            ("expand", "thread expand"),
52            ("oplog", "maintenance oplog recover"),
53        ] {
54            assert_eq!(suggested_command(old), Some(new));
55        }
56    }
57
58    #[test]
59    fn unknown_verbs_have_no_table_hit() {
60        assert_eq!(suggested_command("statuz"), None);
61        assert_eq!(suggested_command("capture"), None);
62    }
63
64    #[test]
65    fn suggestion_text_names_both_verbs() {
66        let text = format_unrecognized_suggestion("save", "capture");
67        assert!(text.contains("unrecognized subcommand 'save'"));
68        assert!(text.contains("capture"));
69    }
70}