いわゆるクリーンアーキテクチャでコマンドラインツールを作る時、標準の "flag"
パッケージのオプションを全部構造体へ転記するのが面倒です。
そこで、次のようにタグにオプション名と Usage を書いておけば、flag.Parse が構造体へ直接フラグ値を代入してくれるというパッケージ: struct2flag をでっちあげました。
(巷にはそういうライブラリも既にありますが、ちょっと多機能すぎるんで…)
type Env struct { B bool `flag:"b,This is a boolean flag"` N int `flag:"n,This is an integer flag"` S string `flag:"s,this is a string flag"` }
使う際は、次のように flag.Parse を呼ぶ前に BindDefault 関数を呼び出すだけで OK です。
var env Env
struct2flag.BindDefault(&env)
flag.Parse()
(全ソース)
構造体フィールドに記載するタグは、他の JSON や XML のタグと共存可能なので
- デフォルト値は設定ファイルから
- デフォルトから変更する指定はコマンドラインオプションで
ということも可能です。
type Env struct { B bool `flag:"b,This is a boolean flag" json:"b"` N int `flag:"n,This is an integer flag" json:"n"` S string `flag:"s,this is a string flag" json:"s"` }
var env Env if data, err := os.ReadFile("example3.json"); err == nil { err = json.Unmarshal(data, &env) } struct2flag.BindDefault(&env) flag.Parse()
(全ソース)
さらにテキストエディターの設定みたいに、環境変数も参照できると便利ですよね。同じようなインターフェイスで環境変数から値をひっぱってくる struct2env というパッケージも作ってみました。
package main import ( "os" "fmt" "encoding/json" "flag" "github.com/hymkor/struct2env" "github.com/hymkor/struct2flag" ) type Env struct { Editor string `env:"EDITOR" flag:"e,specify text editor" "json:"editor"` } func main() { var env Env if jsonText, err := os.ReadFile("example2.json"); err == nil { if err := json.Unmarshal(jsonText, &env); err != nil { fmt.Fprintln(os.Stderr, err.Error()) return } } struct2env.Bind(&env) struct2flag.BindDefault(&env) flag.Parse() fmt.Printf("EDITOR=%#v\n", env.Editor) }
(全ソース)
タグだけで、設定ファイル(JSON)、環境変数、コマンドラインオプションを全部参照するようになりました。やりすぎですね、すみません。
(こうやって仕組みは肥大化し当初の理念が行方不明となりがちになるわけですが、幸い、参照する機能はどれも別のパッケージです。必要な機能のみ取捨選択すればよいでしょう)
See Also
- 最速で書いていただけた解説記事(ありがたや)
- 自作するまで検討したライブラリ(オプション体系が GNU っぽくなってしまったり、
"flag"
の機能まで内包していて、そこまで要らないということで選定候補から外した1)
- なお、struct2flag は "flag" の部分ラッパーに過ぎないので、従来の flag.String など従来の flag 関数も併用できる↩