Search code examples
scalacommand-linescopt

Declare required sub-commands with scopt


I want to declare required sub-commands using scopt (or sth. similar but Scala based).

For example, I want to have the following synopsis (similar to man git, for example):

mycli [--version] [--help] <command> [<args>]

whereas <command> is required to be one the sub-commands, e.g., subA and subB. Something like the following:

private val parser = new OptionParser[Config]("mycli") {
  head(s"${BuildInfo.name} v${BuildInfo.version} is © 2014 by ${BuildInfo.organization}")
  help("help")       text("prints this usage text")
  version("version") text("FIXME”)
  cmd("command") required() action {(c, config) => c match {
    case "subA" => config.copy(mode = "subA") children(
      arg[String]("<files>...") unbounded() optional() action {(rs, config) => config.copy(files = config.files :+ rs) },
      opt[Unit]  ("with-optA")                         action {(_ , config) => config.copy(optA  = true)},
      opt[Unit]  ("with-optB")                         action {(_ , config) => config.copy(optB  = true)})
    case "subB" => //...
    case _      => // ERROR! 
  } }
}

Any hint?

Thanks in advance for your help!


Solution

  • Sure this is kind of tricky, it can lead to many levels of children options.

    Here is my Config class:

    case class Config(cmd:String="", subcmd:String="")
    

    Here is my parser:

    val parser = new scopt.OptionParser[Config]("MyCLI") {
      head("Foo")
      cmd("cmdA") action { (_, c) => c.copy(cmd = "cmdA")
      } children(
        cmd("subA1") action { (_, c) => c.copy(subcmd = "subA1")},
        cmd("subA2") action { (_, c) => c.copy(subcmd = "subA2")}
        )
      cmd("cmdB") action { (_, c) => c.copy(cmd = "cmdA")
      } children(
        cmd("subA1") action { (_, c) => c.copy(subcmd = "subA1")},
        cmd("subA2") action { (_, c) => c.copy(subcmd = "subA2")}
        )
      checkConfig { c => c match {
        case Config("", _) => failure("No command given.")
        case Config(_, "") => failure("Must Choose a subcommand.")
        case _ => success
    
      }}
    }
    

    I hope this helps. I would replace Config.cmd with a sealed trait and objects. I didn't do that so that this would be copyable.

    Each Subcommand can have children of their own, with another command, options or args.

    The call to checkConfig is very important otherwise if no commands are given the application will silently exit.