Skip to content

Latest commit

 

History

History
181 lines (147 loc) · 5.22 KB

commands.md

File metadata and controls

181 lines (147 loc) · 5.22 KB

Commands and subcommands

Start here · How do I...? · Options · Limitations

In normal 1.x code, a command is an object you define by subclassing TBaseCommand. The object owns its registered options; the application owns the command tree and calls the selected object's Execute method.

TBaseCommand
  └── your command class (for example, TGreetCommand)
      ├── Execute
      └── options registered on its instance
your command instance
  └── registered with ICLIApplication

Choose the smallest shape that matches how people invoke the program.

Shape Invocation Use it when
Root command hello --name Ada The program has one default action.
Named command tool greet --name Ada The program is a small toolbox.
Nested command tool repo clone --url … Actions have useful groups, like Git.

Root command

An unnamed descendant is the default action. This complete command pattern defines TRootCommand; its empty name is supplied when the instance is made.

uses
  CLI.Command;

type
  TRootCommand = class(TBaseCommand)
  public
    function Execute: Integer; override;
  end;

function TRootCommand.Execute: Integer;
begin
  WriteLn('Running the default action');
  Result := 0;
end;

This program-setup fragment needs CLI.Interfaces and CLI.Application. It creates the Root object, registers its option on that object, and passes the object to the three-argument factory:

var
  App: ICLIApplication;
  Root: TRootCommand;
begin
  Root := TRootCommand.Create('', 'Run the default action');
  Root.AddFlag('-d', '--verbose', 'Show detailed output');
  App := CreateCLIApplication('hello', '1.0.0', Root);
  Halt(App.Execute);
end.

The root's Execute runs for hello [options]. Its options only belong to the root action; they are not global options inherited by named commands.

Named command

A named descendant is a separately selectable action. This complete command pattern defines the TGreetCommand class and its Execute method:

uses
  CLI.Command;

type
  TGreetCommand = class(TBaseCommand)
  public
    function Execute: Integer; override;
  end;

function TGreetCommand.Execute: Integer;
begin
  WriteLn('Hello from greet');
  Result := 0;
end;

This program-setup fragment creates the Greet instance, gives that instance its --name option, and registers it with App. It needs CLI.Interfaces and CLI.Application:

var
  App: ICLIApplication;
  Greet: TGreetCommand;
begin
  App := CreateCLIApplication('tool', '1.0.0');
  Greet := TGreetCommand.Create('greet', 'Print a greeting');
  Greet.AddStringParameter('-n', '--name', 'Name to greet', False, 'World');
  App.RegisterCommand(Greet);
  Halt(App.Execute);
end.

This accepts tool greet --name Ada. With no root command, tool by itself shows application help instead of running an action.

Subcommand

A nested CLI has a parent command object and a child command object. Both descend from TBaseCommand and must implement Execute; the parent can act as a group even when its method only returns success:

uses
  CLI.Command;

type
  TRepoCommand = class(TBaseCommand)
  public
    function Execute: Integer; override;
  end;

  TCloneCommand = class(TBaseCommand)
  public
    function Execute: Integer; override;
  end;

function TRepoCommand.Execute: Integer;
begin
  Result := 0;
end;

function TCloneCommand.Execute: Integer;
begin
  WriteLn('Cloning a repository');
  Result := 0;
end;

This program-setup fragment builds tool repo clone. It needs CLI.Interfaces and CLI.Application; Clone, not Repo, owns --url:

var
  App: ICLIApplication;
  Repo: TRepoCommand;
  Clone: TCloneCommand;
begin
  App := CreateCLIApplication('tool', '1.0.0');
  Repo := TRepoCommand.Create('repo', 'Repository operations');
  Clone := TCloneCommand.Create('clone', 'Clone a repository');
  Clone.AddUrlParameter('-u', '--url', 'Repository URL', True);
  Repo.AddSubCommand(Clone);
  App.RegisterCommand(Repo);
  Halt(App.Execute);
end.

This accepts tool repo clone --url https://example.com/project.git. A group does not receive options from its children. For a runnable nested example, see SubCommandDemo.

Help and exit codes

tool --help shows application help. tool greet --help shows the selected command. -v/--version print the application version at root, named, and nested scopes (tool repo clone -v), return 0, and skip command execution. These flags are reserved case-insensitively; -V cannot mean verbose. Use --name=--version when that text is an option value. This intentionally corrects the pre-v1.6.1 named-command rejection and conflicting registrations. Return 0 from a command's Execute for success and a non-zero integer for an application failure; the setup fragments use Halt(App.Execute) to forward that result to the shell. The application owns registered commands through its interfaces, so do not manually free them.

See How do I return a non-zero exit code? for a focused method pattern.