Command Line Subcommands in Swift

Here’s the translation of the Go code to Swift, formatted in Markdown suitable for Hugo:

Our first program demonstrates how to use command-line subcommands with their own set of flags. This is similar to how tools like git have different subcommands (e.g., git commit, git push) with their own specific flags.

import Foundation

func main() {
    // We declare a subcommand using CommandLine.arguments
    // and proceed to define new flags specific for this subcommand.
    let fooCmd = ArgumentParser(commandName: "foo", usage: "foo [--enable] [--name NAME]")
    let fooEnable = fooCmd.add(flag: "enable", shortName: "e", kind: Bool.self, usage: "enable")
    let fooName = fooCmd.add(option: "name", shortName: "n", kind: String.self, usage: "name")

    // For a different subcommand we can define different supported flags.
    let barCmd = ArgumentParser(commandName: "bar", usage: "bar [--level LEVEL]")
    let barLevel = barCmd.add(option: "level", shortName: "l", kind: Int.self, usage: "level")

    // The subcommand is expected as the first argument to the program.
    guard CommandLine.arguments.count >= 2 else {
        print("expected 'foo' or 'bar' subcommands")
        exit(1)
    }

    // Check which subcommand is invoked.
    switch CommandLine.arguments[1] {
    // For every subcommand, we parse its own flags and
    // have access to trailing positional arguments.
    case "foo":
        do {
            let result = try fooCmd.parse(Array(CommandLine.arguments.dropFirst()))
            print("subcommand 'foo'")
            print("  enable:", result.get(fooEnable))
            print("  name:", result.get(fooName) ?? "")
            print("  tail:", result.positionalArguments)
        } catch {
            print(error)
            fooCmd.printUsage()
        }
    case "bar":
        do {
            let result = try barCmd.parse(Array(CommandLine.arguments.dropFirst()))
            print("subcommand 'bar'")
            print("  level:", result.get(barLevel) ?? 0)
            print("  tail:", result.positionalArguments)
        } catch {
            print(error)
            barCmd.printUsage()
        }
    default:
        print("expected 'foo' or 'bar' subcommands")
        exit(1)
    }
}

main()

To run the program, compile it and then use the resulting executable:

$ swiftc command_line_subcommands.swift -o command_line_subcommands

First invoke the foo subcommand:

$ ./command_line_subcommands foo --enable --name=joe a1 a2
subcommand 'foo'
  enable: true
  name: joe
  tail: ["a1", "a2"]

Now try bar:

$ ./command_line_subcommands bar --level 8 a1
subcommand 'bar'
  level: 8
  tail: ["a1"]

But bar won’t accept foo’s flags:

$ ./command_line_subcommands bar --enable a1
Error: Unexpected argument --enable
Usage: bar [--level LEVEL]
  -l, --level LEVEL  level

Next we’ll look at environment variables, another common way to parameterize programs.