Command Line Subcommands in Crystal

Here’s the translation of the Go code to Crystal, with explanations in Markdown format suitable for Hugo:

Our first program demonstrates how to create command-line subcommands with their own set of flags. This is similar to how tools like git have subcommands like git commit and git push, each with their own options.

require "option_parser"

# We declare a subcommand using a separate OptionParser
# and proceed to define new flags specific for this subcommand.
foo_cmd = OptionParser.new do |parser|
  parser.banner = "Usage: foo [options]"
  foo_enable = false
  foo_name = ""

  parser.on("-e", "--enable", "Enable") { foo_enable = true }
  parser.on("-n NAME", "--name=NAME", "Name") { |name| foo_name = name }

  parser.on("-h", "--help", "Show this help") do
    puts parser
    exit
  end

  parser.unknown_args do |args|
    puts "subcommand 'foo'"
    puts "  enable: #{foo_enable}"
    puts "  name: #{foo_name}"
    puts "  tail: #{args}"
  end
end

# For a different subcommand we can define different
# supported flags.
bar_cmd = OptionParser.new do |parser|
  parser.banner = "Usage: bar [options]"
  bar_level = 0

  parser.on("-l LEVEL", "--level=LEVEL", "Level") { |level| bar_level = level.to_i }

  parser.on("-h", "--help", "Show this help") do
    puts parser
    exit
  end

  parser.unknown_args do |args|
    puts "subcommand 'bar'"
    puts "  level: #{bar_level}"
    puts "  tail: #{args}"
  end
end

# The subcommand is expected as the first argument
# to the program.
if ARGV.empty?
  puts "expected 'foo' or 'bar' subcommands"
  exit(1)
end

# Check which subcommand is invoked.
case ARGV[0]
# For every subcommand, we parse its own flags and
# have access to trailing positional arguments.
when "foo"
  foo_cmd.parse(ARGV[1..-1])
when "bar"
  bar_cmd.parse(ARGV[1..-1])
else
  puts "expected 'foo' or 'bar' subcommands"
  exit(1)
end

To run the program, save it as command_line_subcommands.cr and use the Crystal compiler:

$ crystal build command_line_subcommands.cr

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: Unknown option '--enable'

This example demonstrates how to create a command-line application with subcommands in Crystal. It uses the OptionParser class to define and parse command-line options for each subcommand. The structure is similar to the original, but it uses Crystal’s idiomatic ways of handling command-line arguments and subcommands.

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