Skip to main content

Generate Commands

Use the scaffolding tool to quickly generate new commands without manually creating files and updating module declarations.

Prerequisites​

Install the scaffolding tool if you haven't already:

cargo install cargo-clawless

Generate a top-level command​

To create a new command at the top level of your CLI:

cargo clawless generate command <name>

For example, to add a version command:

cargo clawless generate command version

This creates src/commands/version.rs with boilerplate code:

use clawless::prelude::*;

#[derive(Debug, Args)]
pub struct VersionArgs {}

#[command]
pub async fn version(args: VersionArgs, context: Context) -> CommandResult {
todo!()
}

It also updates src/commands.rs to include the module declaration:

mod greet;
mod version; // Added automatically

clawless::commands!();

Now you can implement the command:

use clawless::prelude::*;

#[derive(Debug, Args)]
pub struct VersionArgs {}

/// Display version information
#[command]
pub async fn version(args: VersionArgs, context: Context) -> CommandResult {
message!("myapp v{}", env!("CARGO_PKG_VERSION"));
Ok(())
}

Run it:

cargo run -- version

Generate a nested command​

To create a command within a command group, first create the parent command, then the nested command.

Step 1: Create the parent command​

cargo clawless generate command db

This creates src/commands/db.rs:

use clawless::prelude::*;

#[derive(Debug, Args)]
pub struct DbArgs {}

#[command]
pub async fn db(args: DbArgs, context: Context) -> CommandResult {
todo!()
}

Step 2: Create the nested command​

Now you can create a subcommand using a path with /:

cargo clawless generate command db/migrate

This creates src/commands/db/migrate.rs and updates the parent module.

The parent command src/commands/db.rs:

mod migrate;

use clawless::prelude::*;

#[derive(Debug, Args)]
pub struct DbArgs {}

#[command]
pub async fn db(args: DbArgs, context: Context) -> CommandResult {
todo!()
}

The subcommand src/commands/db/migrate.rs:

use clawless::prelude::*;

#[derive(Debug, Args)]
pub struct MigrateArgs {}

#[command]
pub async fn migrate(args: MigrateArgs, context: Context) -> CommandResult {
todo!()
}

And src/commands.rs is updated:

mod greet;
mod version;
mod db; // Added automatically

clawless::commands!();

You can now run:

cargo run -- db migrate

Generate multiple nested commands​

Generate additional commands in the same group:

cargo clawless generate command db/seed
cargo clawless generate command db/reset

These are automatically added as submodules in src/commands/db.rs:

mod migrate;
mod seed;
mod reset;

use clawless::prelude::*;

#[derive(Debug, Args)]
pub struct DbArgs {}

#[command]
pub async fn db(args: DbArgs, context: Context) -> CommandResult {
todo!()
}

Multi-level nesting​

You can nest commands as deeply as needed by creating each level of the hierarchy:

# Create parent commands first
cargo clawless generate command config
cargo clawless generate command config/auth

# Now create the nested commands
cargo clawless generate command config/auth/login
cargo clawless generate command config/auth/logout

This creates:

  • src/commands/config.rs - Top-level parent
  • src/commands/config/auth.rs - Second-level parent
  • src/commands/config/auth/login.rs - Leaf command
  • src/commands/config/auth/logout.rs - Leaf command

Commands available:

cargo run -- config auth login
cargo run -- config auth logout

Naming conventions​

The generator follows Rust naming conventions:

  • Use snake_case for module and function names
  • The generator converts to kebab-case for CLI commands

Example:

cargo clawless generate command deploy_staging

Creates src/commands/deploy_staging.rs with a deploy_staging() function that becomes the deploy-staging CLI command:

cargo run -- deploy-staging

You can also give the name as it looks on the command line. The generator reads deploy-staging and deploy_staging as the same name, and writes the Rust form into the file:

cargo clawless generate command deploy-staging

The generator rejects a name for which it cannot write a Rust function. A name must start with a letter, must end with a letter or a digit, and must have one hyphen between two words.

What gets generated​

For each command, the generator creates:

  1. A command file with:

    • Imports from clawless::prelude::*
    • An Args struct with #[derive(Debug, Args)]
    • A command function with #[command] and todo!() placeholder
  2. Module declarations in the appropriate parent file

  3. Directory structure matching the command hierarchy

You then fill in:

  • Doc comments for help text
  • Arguments in the Args struct
  • Implementation in the command function

Cleaning up boilerplate​

The generated code includes these derives by default:

#[derive(Debug, Args)]

You can add more derives if needed (Clone, PartialEq, etc.) or remove them if they're not necessary for your use case.

See also​