Skip to content

App Dispatch: Subcommand Resolution and execution routing

The main entry point for running a clish application is calling the run() method on the App structure. This method collects environment arguments, handles global flags (like version and help requests), and routes commands to their execution closures.

This page explains how the dispatch sequence functions, including the differences between Multi-Command and One-Shot execution modes.


The Execution Flowchart

Here is how App::run() evaluates command-line arguments at startup:

flowchart TD
    A([app.run]) --> B[Read argv]
    B --> C{Oneshot Mode?}

    C -->|Yes| D[Startup Sanity Checks]
    D --> E{Has Help or Version?}
    E -->|Yes| F[Print Info and Exit 0]
    E -->|No| G[Execute Closure with remaining args]
    G --> H{Succeed?}
    H -->|Yes| I([Exit 0])
    H -->|No| J[Print Error and Exit 1]

    C -->|No| K{Too few args?}
    K -->|Yes| L[Print App Help and Exit 1]
    K -->|No| M{Is Help or Version?}
    M -->|Yes| N[Print Info and Exit 0]
    M -->|No| O[Match Subcommand]
    O --> P{Subcommand Found?}
    P -->|Yes| Q[Execute Closure with remaining args]
    P -->|No| R[Print Error and Exit 1]

One-Shot Mode (via app!(cmd_func))

One-shot mode is designed for simple, single-purpose CLI utilities. Instead of requiring a subcommand prefix (e.g., myapp deploy --force), the application parses and executes your command function directly from the root arguments (e.g., myapp --force).

Startup Sanity Checks

Because one-shot mode runs a single command, the macro and runtime perform verification checks at startup to prevent misconfigurations: 1. Registry Size: The registry must contain exactly one command. If multiple functions are annotated with #[command], the program panics, instructing you to use multi-command mode. 2. No Aliases: The oneshot command must not configure any aliases. 3. Name Matching: The command name must match the function name (no custom name = "custom" attributes). 4. No Hidden or Deprecated Flags: The command cannot be hidden or deprecated.

If any check fails, the application panics immediately before parsing arguments to help the developer fix the configuration.


Multi-Command Mode (via app!())

Multi-command mode is the default execution style. It uses the second token of the command line (argv[1]) as a subcommand route name.

1. Global Flags Resolution

Before matching subcommands, the runner checks for global flags: * Version Flags: If the user passes --version or -V as the only argument, the app prints its name and version and exits. If --verbose or -v is also present, it prints the description, OS architecture, and target details. * Help Flags: If the first argument is --help or -h, the app checks the next token. If it matches a subcommand, it displays that subcommand's help. Otherwise, it prints the general application help screen.

2. Command Lookup

The runner compares argv[1] against: 1. The primary name of each command entry. 2. The list of aliases configured for each command entry.

If a match is found, the runner checks the next argument. If it is --help or -h, it intercepts execution to display the subcommand help screen. Otherwise, it executes the command closure, passing all remaining arguments (argv[2..]).

3. Unknown Subcommand Routing

If argv[1] does not match any registered command, the app routes execution to the error handling pipeline, printing an UnknownCommand diagnostic with carets pointing to the unrecognized subcommand, then exits with code 1.


Standard Termination Codes

clish respects shell execution conventions: * Exit Code 0: Returned on successful command execution, successful help output requests (--help), or version queries. * Exit Code 1: Returned on parsing failures, constraint violations, unknown subcommands, or if no subcommand is provided when running a multi-command app.


This completes the Advanced internals tutorial! You now have a complete, end-to-end understanding of how clish generates, tokenizes, validates, and dispatches CLI requests.