[Swift] ArgumentParserを使って自作コマンドを作る

SwiftではArgumentParserを使用すると簡単にSwiftで自作コマンドの作成が可能です。
ArgumentParserとはAppleが作った公式のライブラリで、リポジトリはこちらです。

https://github.com/apple/swift-argument-parser

今回はSwift Package ManagerとArgumentParserを使用してコマンドを作る方法を紹介します！

# 導入
まず新しくコマンド用にディレクトリを作成して、そこで以下のようなコマンドを叩きます。 
```Swift
swift package init --name <コマンドの名前> --type executable
```
叩いたら、次にArgumentParserをPackage.swiftに追加していきます。
```Swift
import PackageDescription

let package = Package(
    name: "TestCLITool",
    products: [
        .executable(name: "TestCLITool", targets: ["TestCLITool"])
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2")
    ],
    targets: [
        // Targets are the basic building blocks of a package, defining a module or a test suite.
        // Targets can depend on other targets in this package and products from dependencies.
        .executableTarget(
            name: "TestCLITool",
            dependencies: [
                .product(name: "ArgumentParser", package: "swift-argument-parser")
            ]
        ),
    ]
)

```
今回はTestCLIToolという名前のCLIツールを作成することにしました。
# 実装
とりあえずHello Worldを出力するCLIツールを作成してみましょう。

```Swift
import Foundation
import ArgumentParser

@main
struct TestCLITool: ParsableCommand {
    mutating func run() throws {
        print("Hello World!!")
    }
}
```
`mutating func run()`のところにコマンドが呼ばれた時に実行したい処理を記述します。

## 実行
このコマンドで作ったCLIツールを呼び出すことができます。
```
$ swift run TestCLITool
```
実行してみると、、
![スクリーンショット 2023-06-04 16.54.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627061/1bd7b7c1-a569-bcc7-0617-a9fcd6e4b370.png)
しっかりとHello worldが出力されています。

また、swift-argument-parserは便利なことに自動でコマンドのヘルプを作成してくれます。
このコマンドのヘルプを出力するには、
```Swift
$ swift run TestCLITool -h
```
これを実行すると、
![スクリーンショット 2023-06-04 16.55.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627061/b0d37d56-9de0-db76-d450-87b2c823cdd5.png)
このようにヘルプを表示することができました！！

## 引数, オプション, Flagを追加する
ArgumentParserはPropertyWrapperを使用することで、簡単にコマンドに引数、オプションを追加することが可能です。

### 引数
まずは引数を追加していきます。
```Swift
import Foundation
import ArgumentParser

@main
struct TestCLITool: ParsableCommand {
    @Argument(help: "名前を入力")
    var text: String

    mutating func run() throws {
        print("Hello! \(text)")
    }
}
```
PropertyWrapperのArgumentを使うことで引数を追加できます。
また、引数にhelpの情報を付け足すことができます。コマンドのヘルプを実行すると、
```
USAGE: test-cli-tool <text>

ARGUMENTS:
  <text>                  名前を入力

OPTIONS:
  -h, --help              Show help information.
```
このようにヘルプが追加されていることがわかります。

```
$ swift run TestCLITool たけし
```
これを実行すると、
![スクリーンショット 2023-06-04 17.34.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627061/17f42df3-0285-e193-aa02-feab4a1b27ef.png)
`Hello! たけし`
と出力されていることがわかります。

### オプション
次にオプションを追加する方法をご紹介します。
```Swift
import Foundation
import ArgumentParser

@main
struct TestCLITool: ParsableCommand {
    @Option(name: .shortAndLong, help: "名前を入力")
    var text: String?

    mutating func run() throws {
        if let text {
            print("Hello! \(text)")
        } else {
            print("Hello World!")
        }
    }
}
```
PropertyWrapperのOptionを使うことでコマンドのオプションを追加できます。
今回はnameに`.shortAndLong`を指定しているので、
```
$ swift run TestCLITool -t たけし
$ swift run TestCLITool --text たけし
```
このように、`--text`とその短縮版の`-t`の両方が利用可能です。
これを実行すると、
![スクリーンショット 2023-06-04 18.22.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627061/32577ad6-a749-c91a-460e-b0f0fd444770.png)
オプションを追加できました！！
そのほかにも`name`には以下が指定可能です。
|  name  |  意味  |
| ---- | ---- |
|  long  |  lowerCamelCaseで書かれたproperty名をハイフン区切りにのオプションにしてくれる  |
|  short  |  短縮版  |
|  shortAndLong  |  shortとlong  |
|  customLong(_:withSingleDash:)  |  オプション名をカスタムで指定できる  |
|  customShort(_:allowingJoined:)  |  オプション名をカスタムで1文字指定できる  |

### Flag
次にFlagを追加する方法についてご紹介します。 
```Swift
import Foundation
import ArgumentParser

@main
struct TestCLITool: ParsableCommand {
    @Flag(name: [.customLong("goodbye"), .customShort("g")], help: "Goodbye Worldを出力するかどうか")
    var goodbyeWorld: Bool = false

    mutating func run() throws {
        if goodbyeWorld {
            print("Goodbye world!")
        } else {
            print("Hello World!")
        }
    }
}
```
PropertyWrapperのFlagを使用することで、コマンドにフラグを追加できます。
以下のいずれかでコマンドを実行できます。
```
$ swift run TestCLITool --goodbye
$ swift run TestCLITool -g
```
これを実行すると、
![スクリーンショット 2023-06-04 18.43.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627061/57d429d7-ae53-d561-c19b-b33295555f17.png)
期待した動作になりました。

## コマンドをグローバルで使えるようにする
自作したコマンド、`$ swift run <コマンド名>`じゃなくて`$ TestCLITool --goodbye`みたいに使いたい！って方いるとおもいます。
それをするには単にいろんなコマンドが入ってる
`/usr/local/bin`
にビルドしたバイナリを入れてあげれば良いです。
このようなMakefileを作成します。
```Makefile: Makefile
COMMAND_NAME = TestCLITool

.PHONY: release
release:
	swift build -c release
	sudo cp .build/release/$(COMMAND_NAME) /usr/local/bin/$(COMMAND_NAME)
```
`$ make release`と打つことで、ターミナルでこのコマンドが使えるようになります。
これはこの記事を参考にしました。

https://zenn.dev/kyome/articles/ef882158791c59

## コマンド名を変える
このようにしてコマンド名を変えます。
```Swift
import Foundation
import ArgumentParser

@main
struct TestCLITool: ParsableCommand {
    static var configuration = CommandConfiguration(commandName: "ryu-test")

    mutating func run() throws {
        print("Hello World!")
    }
}
```
他にもCommandConfigurationを使用してさまざまなコマンドの設定を変更可能となっています。

# さいごに
今回はArgumentParserを使用して自作CLIツールの作成の仕方を解説しました。簡単にCLIツールを作ることができました！
自分は他にもさまざまなCLIツールを自作しているので、よかったら自分のGithubを見ていただけると嬉しいです。

https://github.com/Ryu0118

CLIツール自作するのめっちゃ楽しいので、みなさんもこれを機にCLIツールを自作してみましょう！