demo_errors/
demo_errors.rs

1//! Demonstration of facet-args colored help and error messages
2
3use facet::Facet;
4use facet_args as args;
5use miette::Report;
6
7/// Git-like CLI with subcommands
8#[derive(Facet, Debug)]
9#[allow(dead_code)]
10struct GitArgs {
11    /// Show version information
12    #[facet(args::named)]
13    version: bool,
14
15    /// Git command to run
16    #[facet(args::subcommand)]
17    command: GitCommand,
18}
19
20/// Available git commands
21#[derive(Facet, Debug)]
22#[repr(u8)]
23#[allow(dead_code)]
24enum GitCommand {
25    /// Clone a repository
26    Clone {
27        /// URL of the repository to clone
28        #[facet(args::positional)]
29        url: String,
30        /// Directory to clone into
31        #[facet(default, args::positional)]
32        directory: Option<String>,
33    },
34    /// Show commit history
35    Log {
36        /// Number of commits to show
37        #[facet(args::named, args::short = 'n')]
38        count: Option<usize>,
39        /// Show one line per commit
40        #[facet(args::named)]
41        oneline: bool,
42    },
43    /// Manage remotes
44    Remote {
45        /// Remote subcommand
46        #[facet(args::subcommand)]
47        action: RemoteAction,
48    },
49}
50
51/// Remote management commands
52#[derive(Facet, Debug)]
53#[repr(u8)]
54#[allow(dead_code)]
55enum RemoteAction {
56    /// Add a new remote
57    Add {
58        /// Name of the remote
59        #[facet(args::positional)]
60        name: String,
61        /// URL of the remote
62        #[facet(args::positional)]
63        url: String,
64    },
65    /// Remove a remote
66    #[facet(rename = "rm")]
67    Remove {
68        /// Name of the remote to remove
69        #[facet(args::positional)]
70        name: String,
71    },
72    /// List all remotes
73    #[facet(rename = "ls")]
74    List {
75        /// Show verbose output
76        #[facet(args::named, args::short = 'v')]
77        verbose: bool,
78    },
79}
80
81fn main() {
82    let scenarios = [
83        ("Top-level help", vec!["--help"]),
84        ("Missing required subcommand", vec![]),
85        ("Unknown subcommand", vec!["notacommand"]),
86        ("Subcommand help", vec!["clone", "--help"]),
87        ("Nested subcommand help", vec!["remote", "add", "--help"]),
88    ];
89
90    for (description, args_vec) in &scenarios {
91        println!("\n{}", "=".repeat(80));
92        println!("SCENARIO: {}", description);
93        println!("Args: {:?}", args_vec);
94        println!("{}", "=".repeat(80));
95
96        let result = args::from_slice::<GitArgs>(args_vec);
97        match result {
98            Ok(_) => println!("✓ Successfully parsed arguments"),
99            Err(e) => {
100                let is_help = e.is_help_request();
101                println!(
102                    "\n{} {}",
103                    if is_help {
104                        "ℹ️  Help requested"
105                    } else {
106                        "❌ Error"
107                    },
108                    if is_help {
109                        "(exit code: 0)"
110                    } else {
111                        "(exit code: 1)"
112                    }
113                );
114                println!("\n{:?}\n", Report::new(e));
115            }
116        }
117    }
118
119    println!("\n{}", "=".repeat(80));
120    println!("Summary:");
121    println!("  • Help requests exit with code 0 and show colored help");
122    println!("  • Errors exit with code 1 and show diagnostic with suggestions");
123    println!("  • Subcommand help works with: <subcommand> --help");
124    println!("{}", "=".repeat(80));
125}