search:

Scala용 Airlift/Airline 예제 코드

10 Aug 2019

목차

개요

Druid의 소스 코드를 보다가 Airline을 알게되었다.

Airline의 github을 보면, Airline에 대해 “Airline is a Java annotation-based framework for parsing Git like command line structures”라고 설명되어있다.

git의 사용법을 보면 명령이 다양하고 명령마다 파라미터가 서로 다른 것을 볼 수 있는데, 이런 것을 일반적인 argument parsing library를 사용하여 구현하기엔 매우 복잡하다.

참고로 Druid에서 Airline을 어떻게 사용하는지 알고자 한다면 Main.java를 보면 된다.

Druid에서 Airline을 잘 사용하고 있고, 성숙도도 높아보여서 다음 프로젝트에서는 나도 도입해보고자 했다. 그런데 Airline은 Java로 만들어져있고 나는 Scala를 사용하는 구나. Scala용 Airline은 찾을 수 없고, Scala가 어차피 JVM에서 도는 거라 Airline 예제 코드를 Scala 코드로 바꿔보려해도 능력이 안되서;

암튼 Scala 코드로 짠 건 없는지 검색을 해봤는데 찾기 어려웠고, 누군가 github gist에 올린 걸 겨우 찾아냈다. https://gist.github.com/jw3/14e8cc45f7f5fe9161ba

4년 전 코드라서 그런지 @Arguments 부분이 잘 돌아가질 않았는데, Data Type을 Seq[String]에서 java.util.List[String]로 바꾸니 잘 돌아갔다.

소스 코드

아래는 gist에서 찾은 코드를 약간 손 본 코드이다. Scala 2.11.8에서 잘 도는 것 확인 완료.

import io.airlift.airline._

// 출처: https://gist.github.com/jw3/14e8cc45f7f5fe9161ba
// 위에거에서 Seq[String]만 java.util.List[String]으로 변경했음

/**
  * airline example in scala
  * https://github.com/airlift/airline
  * @author wassj
  */
object AirlineTest {

  def main(args: Array[String]) {

    val builder = Cli.builder[Runnable]("git")
      .withDescription("the stupid content tracker")
      .withDefaultCommand(classOf[Help])
      .withCommands(classOf[Help], classOf[Add])

    builder.withGroup("remote")
      .withDescription("Manage set of tracked repositories")
      .withDefaultCommand(classOf[RemoteShow])
      .withCommands(classOf[RemoteShow], classOf[RemoteAdd])

    val gitParser: Cli[Runnable] = builder.build()

    gitParser.parse(args: _*).run();
  }

  trait GitCommand extends Runnable {
    @Option(`type` = OptionType.GLOBAL, name = Array("-v"), description = "GitCommand mode")
    var GitCommand: Boolean = false

    def run(): Unit = {
      println(s"class=${getClass.getSimpleName}")
      println(s"GitCommand=${GitCommand}")
    }
  }

  @Command(name = "add", description = "Add file contents to the index")
  class Add extends GitCommand {
    @Arguments(description = "Patterns of files to be added")
    var patterns: java.util.List[String] = _

    @Option(name = Array("-i"), description = "Add modified contents interactively.")
    var interactive: Boolean = _

    override def run(): Unit = {
      println(s"class=${getClass.getSimpleName}")
      println(s"patterns = ${patterns}")
      println(s"interactive = ${interactive}")
    }
  }

  @Command(name = "show", description = "Gives some information about the remote <name>")
  class RemoteShow extends GitCommand {
    @Option(name = Array("-n"), description = "Do not query remote heads")
    var noQuery: Boolean = false

    @Arguments(description = "Remote to show")
    var remote: String = _

    override def run(): Unit = {
      println(s"class=${getClass.getSimpleName}")
      println(s"noQuery = ${noQuery}")
      println(s"remote = ${remote}")
    }
  }

  @Command(name = "track", description = "Adds a remote")
  class RemoteAdd extends GitCommand {
    @Option(name = Array("-u"), description = "Track only a specific branch")
    var branch: String = _

    @Arguments(description = "Remote repository to add")
    var remote: java.util.List[String] = _

    override def run(): Unit = {
      println(s"class=${getClass.getSimpleName}")
      println(s"branch = ${branch}")
      println(s"remote = ${remote}")
    }
  }
}

실행 예

본인도 처음에 위의 코드를 볼 때 어떻게 argument를 넘겨야할는지, 어떤 변수에 어떤 값이 할당되는지 살짝 헷갈렸기에 사용 예를 적어 놓는다.

$ java -cp ... AirlineTest add
class=Add
patterns = null
interactive = false

$ java -cp ... AirlineTest add -i file1 file2 file3
class=Add
patterns = [file1, file2, file3]
interactive = true

$ java -cp ... AirlineTest remote track
class=RemoteAdd
branch = null
remote = null

$ java -cp ... AirlineTest remote track -u mybranch remote1 remote2
class=RemoteAdd
branch = mybranch
remote = [remote1, remote2]