golangでインタラクティブなCLIツールをサクッと作る
チーム開発において、ちょっとした作業を自動化するためのツールを作ることが多々あります。エンジニアのみが使用する場合、ざっくり作成したCLIツールやshellscriptで問題ないですが、非エンジニアの方含めて使えるようなツールとなると、環境構築などが不要な実行ファイル形式であった方が良いですし、コマンドを打たなくても使える方が好ましいです。
しかし、GUIツールを作るとなると、工数やバグ発生率がぐんと上がってしまいます。そのため、クロスコンパイル可能なgo言語を使ってサクッと作れるようなインタラクティブなCLIツールを作成するライブラリがないかと探していました。その中で、promptuiというライブラリがシンプルで良さそうだったため紹介します。
本記事の内容
すること
- goプロジェクト作成
- promptuiで入力をstring型で取得する
- promptuiで選択肢を矢印入力で選択する
扱わないこと
- golangのコンパイル環境の作成
promptui
promptuiは、文字入力(Prompt)と矢印キーの選択(Select)による入力をインタラクティブに扱えるgolangライブラリです。できることがシンプルであり学習コストが小さいため、サクッとCLIツールを作ることができます。
プロジェクト作成からpromptui導入まで
まずはプロジェクトを作成し、promptuiを依存関係に追加します。
PROJECT_NAME=golang-promptui-example
mkdir $PROJECT_NAME
cd $PROJECT_NAME
go mod init $PROJECT_NAME
go get https://github.com/manifoldco/promptui
文字入力を扱う
以下は、文字入力を扱う場合の公式のサンプルです。
流れとしては以下の3ステップです。
- validationを作成
- promptオブジェクトを作成
- prompt.Run()で入力を取得
package main
import (
"errors"
"fmt"
"strconv"
"github.com/manifoldco/promptui"
)
func main() {
// 入力が不正な場合errorを返す関数を作成
validate := func(input string) error {
_, err := strconv.ParseFloat(input, 64)
if err != nil {
return errors.New("Invalid number")
}
return nil
}
// インタラクションの表示やバリデーションを設定
prompt := promptui.Prompt{
Label: "Number", // 表示する文言
Validate: validate, // validate
}
result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("You choose %q\n", result)
}
以上の例は最小限構成ですが、promptui.Promptの設定値としては以下のようなものがあり、Templateでバリデーション状況によって色を変えたり、IsConfirmで y/N形式の入力を扱うこともできます。
key | type | description |
---|---|---|
Label | interface{} | promptに表示されるラベル(基本string) |
Default | string | デフォルト値 |
AllowEdit | bool | falseの時、文字入力時にデフォルト値をクリアする |
HideEnterd | bool | trueの時、入力完了後に入力値を隠す |
Templates | *promptui.PromptTemplate |
リッチな表現をするためのtemplate |
Mask | rune | パスワードなど、値を隠したい時にマスクする |
IsConfirm | bool | trueの時、y/N での入力になる |
IsVimMode | bool | 左右移動がvim形式で可能になる(ぶっちゃけ不要) |
Pointer | promptui.Pointer | カーソルをカスタマイズできる(DefaultCursor, BlockCursor, PipeCursorが用意されている) |
Templateは以下のように、golangのテンプレート構文を利用して、validationの可否によって色を変えるなどができます。
templates := &promptui.PromptTemplates{
Prompt: "{{ . }} ",
Valid: "{{ . | green }} ",
Invalid: "{{ . | red }} ",
Success: "{{ . | bold }} ",
}
矢印キーによる選択型の入力
選択では、バリデーションは不要であるため、promptインスタンスに、選択肢を配列で与えて、prompt.Run()を実行することで、選択した選択肢の順番(index)と文字列(result)が返ります。ここでは配列を文字列で与えていますが、任意オブジェクトの配列を渡すことができます。(resultにはfmt.Sprintf("%v", item)
がかかってstringで帰ってくるため、indexを利用して値を取り出すのが良いと思われます。)
package man
import (
"fmt"
"github.com/manifoldco/promptui"
)
func main() {
prompt := promptui.Select{
// 選択肢のタイトル
Label: "Select Day",
// 選択肢の配列
Items: []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday"},
}
idx, result, err := prompt.Run() //入力を受け取る
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("You choose no.%d %q\n",idx, result)
}
promptui.Selectの設定値としては以下のようなものがあります。
key | type | description |
---|---|---|
Label | interface{} | promptに表示されるラベル 基本はstringだがTemplateを使うならstructでも良い |
Items | 配列 | 選択肢の配列 |
Size | int | スクロールなしで表示する選択肢の数(default=5) |
CursorPos | int | 初期のカーソル位置 |
IsVimMode | bool | Vim形式で移動できる |
HideHelp | bool | ヘルプ情報を隠すかどうか |
HideSelected | bool | trueの場合選択後に選択した内容を隠す |
Templates | *promptui.SelectTemplate |
リッチな表現をするためのtemplate |
Keys | *promptui.SelectKey |
選択時に操作するキーのカスタム設定 |
Searcher | promptui.list.Searcher |
(input string,index int) ->bool 型の関数を渡すことで、検索を実装できる(/ を押すとSearchModeに入る) |
StartInSearchMode | bool | SearchModeから始めるかSelectModeから始めるか |
Pointer | promptui.Pointer |
カーソルの種類 |
また、選択肢以外にユーザが入力して追加できるようにする場合はpromptui.SelectWithAdd
を使用します。
これらの基本的な書き方は公式の例を参照すると良さそうです。(冒頭のgifもこのサンプルをもとにしています)
終わりに
本記事では、golangでpromptuiを使用することで、インタラクティブなCLI ツールを作成する方法についてまとめました。
golangはクロスコンパイルが可能かつ、rustほど型が厳しくない言語であるため、サクッとツールを作成する方法を知っておくと何かと便利だと思います。著者はgolangの扱いにはそこまで慣れていませんが、シンプルな言語なだけあり、短い時間でツールを作成できました。この記事が何らか役に立てば幸いです。
Discussion