Skip to main content

Naming Conventions

Clawless follows a simple naming convention: use standard Rust naming (snake_case), and it automatically converts to CLI conventions (kebab-case). This section explains how different names map to your CLI.

Function names → Command names​

The function name becomes the command name, with snake_case converted to kebab-case:

#[command]
pub async fn greet(args: GreetArgs, context: Context) -> CommandResult {
// ...
}

Results in: myapp greet

Multi-word functions:

#[command]
pub async fn deploy_staging(args: DeployArgs, context: Context) -> CommandResult {
// ...
}

Results in: myapp deploy-staging

The conversion is automatic - always write functions in snake_case.

Names that Clawless rejects:

A leading underscore, a trailing underscore, and two underscores in a row have no readable translation, because each one would put a hyphen where a command name must not have one. The macro reports a compile error for _deploy, deploy_, and deploy__staging. Rename the function so that it starts and ends with a letter, and so that one underscore separates two words.

Reserved words:

A command that shares its name with a Rust keyword needs a raw identifier. The r# prefix belongs to Rust and not to the command line, so Clawless removes it:

#[command]
pub async fn r#type(args: TypeArgs, context: Context) -> CommandResult {
// ...
}

Results in: myapp type

File names → Command names​

File names follow the same pattern:

  • greet.rs → myapp greet
  • deploy_staging.rs → myapp deploy-staging
  • user_profile.rs → myapp user-profile

Important: The file name doesn't directly become the command name. The function name inside the file does. However, by convention, the file name should match the function name:

// src/commands/deploy_staging.rs

#[command]
pub async fn deploy_staging(args: Args, context: Context) -> CommandResult {
// File name matches function name
}

Directory names → Subcommand groups​

Directory names become subcommand groups:

  • db/ → myapp db ...
  • user_management/ → myapp user-management ...

Example structure:

src/commands/
├── db.rs (contains: pub async fn db(...))
└── db/
├── migrate.rs
└── seed.rs

Results in:

  • myapp db
  • myapp db migrate
  • myapp db seed

The file db.rs contains the parent command function and declares the submodules.

Arguments struct names​

Arguments structs follow the convention: <Command>Args

#[derive(Debug, Args)]
pub struct GreetArgs {
name: String,
}

#[command]
pub async fn greet(args: GreetArgs, context: Context) -> CommandResult {
// ...
}

This is a convention, not a requirement. You can name the struct anything, but CommandArgs is the recommended pattern for consistency.

For multi-word commands:

  • deploy_staging → DeployStagingArgs
  • user_profile → UserProfileArgs

Doc comments → Help text​

Doc comments on functions become command descriptions:

/// Greet the user by name
///
/// This command prints a friendly greeting message.
#[command]
pub async fn greet(args: GreetArgs, context: Context) -> CommandResult {
// ...
}

Running myapp greet --help shows:

Greet the user by name

This command prints a friendly greeting message.

The first line is the short description (shown in command lists), and subsequent paragraphs become the long description (shown in --help).

Aliases​

Aliases are explicitly defined, not derived from names:

#[command(alias = "g")]
pub async fn greet(args: GreetArgs, context: Context) -> CommandResult {
// ...
}

Results in both myapp greet and myapp g working.

Examples​

Simple command​

// src/commands/version.rs

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

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

Command: myapp version

Multi-word command​

// src/commands/check_updates.rs

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

/// Check for available updates
#[command]
pub async fn check_updates(args: CheckUpdatesArgs, context: Context) -> CommandResult {
message!("Checking for updates...");
Ok(())
}

Command: myapp check-updates

Nested command group​

In src/commands/user_management.rs:

mod create_user;

use clawless::prelude::*;

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

/// User management commands
#[command(require_subcommand)]
pub async fn user_management(args: UserManagementArgs, context: Context) -> CommandResult {
Ok(())
}

In src/commands/user_management/create_user.rs:

#[derive(Debug, Args)]
pub struct CreateUserArgs {
username: String,
}

/// Create a new user
#[command]
pub async fn create_user(args: CreateUserArgs, context: Context) ->
CommandResult {
message!("Creating user: {}", args.username);
Ok(())
}

Commands:

  • myapp user-management
  • myapp user-management create-user

What's next​

Now that you understand naming conventions, learn about: