Kingpin

Goのコマンドライン引数パーサー。流暢なインターフェースとPOSIX/GNU規約のサポートが特徴です。

goclifluent-interfaceposix

フレームワーク

Kingpin

概要

Kingpinは、Goのコマンドライン引数パーサーです。流暢なインターフェース(Fluent Interface)とPOSIX/GNU規約のサポートが特徴です。シンプルで直感的なAPIを提供し、複雑なコマンドライン構造を簡単に構築できます。現在はメンテナンスモードとなっており、新規プロジェクトでは他の選択肢が推奨されています。

詳細

Kingpinは長年にわたってGoコミュニティで使用されてきた実績のあるCLIライブラリです。流暢なインターフェースにより、可読性の高いコマンド定義が可能で、多くのオープンソースプロジェクトで採用されました。しかし、現在は積極的な開発が終了し、メンテナンスモードに移行しています。新しいプロジェクトでは、より活発に開発されているcobraやurfave/cliの使用が推奨されています。

主な特徴

  • 流暢なインターフェース: メソッドチェーンによる直感的なAPI
  • POSIX/GNU規約サポート: 標準的なコマンドライン規約に準拠
  • サブコマンド: 階層的なコマンド構造をサポート
  • カスタムバリデーション: 独自の値検証ロジックの実装が可能
  • シェル補完: Bash、Zsh、Fish、PowerShellでの補完をサポート
  • 環境変数サポート: フラグのデフォルト値を環境変数から取得
  • ヘルプ生成: 自動的なヘルプメッセージとUsageの生成

メリット・デメリット

メリット

  • 可読性: 流暢なインターフェースによる理解しやすいコード
  • 豊富な機能: 本格的なCLIアプリケーションに必要な機能を網羅
  • 標準準拠: POSIX/GNU規約に準拠した一貫性のある動作
  • 柔軟性: カスタムバリデーションや値パーサーの実装が可能
  • 実績: 多くのプロジェクトでの採用実績

デメリット

  • メンテナンス状況: 現在はメンテナンスモードで新機能開発は停止
  • コミュニティ: アクティブなコミュニティサポートが限定的
  • 将来性: 長期的なサポートに不安がある
  • 学習コスト: 新規プロジェクトでは他の選択肢の方が推奨される

主要リンク

書き方の例

package main

import (
	"fmt"
	"net"
	"time"

	"github.com/alecthomas/kingpin/v2"
)

var (
	// グローバルフラグ
	debug   = kingpin.Flag("debug", "デバッグモードを有効にする").Bool()
	timeout = kingpin.Flag("timeout", "ping待機のタイムアウト").
		Default("5s").
		Envar("PING_TIMEOUT").
		Short('t').
		Duration()
	
	// 引数
	ip    = kingpin.Arg("ip", "ping先のIPアドレス").Required().IP()
	count = kingpin.Arg("count", "送信するパケット数").Int()
)

func main() {
	// バージョン設定
	kingpin.Version("0.0.1")
	
	// 引数をパース
	kingpin.Parse()
	
	// 実行
	fmt.Printf("ping実行: %s (タイムアウト %s, 回数 %d)\n", *ip, *timeout, *count)
	
	if *debug {
		fmt.Println("デバッグモードが有効です")
	}
}

// サブコマンドの例
func withSubcommands() {
	var (
		app = kingpin.New("chat", "コマンドライン チャットアプリケーション")
		
		// グローバルフラグ
		serverIP = app.Flag("server", "サーバーアドレス").Default("127.0.0.1").IP()
		debug    = app.Flag("debug", "デバッグモードを有効にする").Bool()
		
		// registerコマンド
		register     = app.Command("register", "新しいユーザーを登録")
		registerNick = register.Arg("nick", "ユーザーのニックネーム").Required().String()
		registerName = register.Arg("name", "ユーザーの名前").Required().String()
		
		// postコマンド
		post        = app.Command("post", "チャンネルにメッセージを投稿")
		postChannel = post.Arg("channel", "投稿先チャンネル").Required().String()
		postText    = post.Arg("text", "投稿するテキスト").Strings()
		postImage   = post.Flag("image", "投稿する画像").File()
	)
	
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case register.FullCommand():
		fmt.Printf("ユーザー登録: %s (%s)\n", *registerNick, *registerName)
		
	case post.FullCommand():
		fmt.Printf("チャンネル %s に投稿\n", *postChannel)
		if *postImage != nil {
			fmt.Println("画像付き投稿")
		}
		if len(*postText) > 0 {
			fmt.Printf("メッセージ: %s\n", strings.Join(*postText, " "))
		}
	}
}