背景
のように設定ファイルをYAMLで管理するパターンは良くありますが、設定項目が増えるほど運用つらくなるので、あまり弄らない項目に対してはデフォルトを用意し、値がなければそれを設定したいケースがあります。
イメージとしては以下です。
type Config struct { Endpoint string `yaml:"endpoint"` Timeout time.Duration `yaml:"timeout" default:"10s"` Retry int `yaml:"retry" default:"3"` }
しかしgoのyamlパッケージgopkg.in/yaml.v3
はデフォルト値のタグをサポートしていないため、自分で行う必要があります。
今回はその方法を紹介します。
環境
- Go 1.20.4
- gopkg.in/yaml.v3 v3.0.1
- creasty/defaults v1.7.0
方法
creasty/defaultsを使う
GitHub - creasty/defaults: Initialize structs with default values
を使うとstructのタグにデフォルト値があった場合にそれを使うようにしてくれます。
type Sample struct { Name string `default:"John Smith"` Age int `default:"27"` Gender Gender `default:"m"` Working bool `default:"true"` } func main() { obj := &Sample{} if err := defaults.Set(obj); err != nil { panic(err) } out, err := json.MarshalIndent(obj, "", " ") if err != nil { panic(err) } fmt.Println(string(out)) }
これを実行すると以下のようになります。
{ "Name": "John Smith", "Age": 27, "Gender": "m", "Working": true, }
YAMLに適用する
gopkg.in/yaml
はUnmarshalYAML() を実装することで内部処理をカスタマイズすることができます。
なのでその中で defaults.Set() を呼び出します。
type Config struct { Endpoint string `yaml:"endpoint"` Timeout time.Duration `yaml:"timeout" default:"10s"` Retry int `yaml:"retry" default:"3"` } func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { defaults.Set(c) type plain Config if err := unmarshal((*plain)(c)); err != nil { return err } return nil }
動作確認
設定あり
--- endpoint: https://example.com timeout: 30s retry: 5
{ Endpoint:"https://example.com", Timeout:30000000000, Retry:5 }
指定した値が設定されています。
設定なし
--- endpoint: https://example.com
デフォルト値があるtimeoutやretryを削ります。
{ Endpoint:"https://example.com", Timeout:10000000000, Retry:3 }
デフォルト値が設定されています。
Zero値
Goにおける初期値をYAMLで設定するとどうなるでしょうか?
--- endpoint: https://example.com timeout: 0s retry: 0
{ Endpoint:"https://example.com", Timeout:0, Retry:0 }
デフォルト値ではなく、期待通りZero値が設定されています。
その他
サンプルコード
今回のサンプルコードはこちら
まとめ
GoでYAMLファイルを読み込んだ時にデフォルト値を設定する方法を紹介しました。