Carpe Diem

備忘録

GoでYAMLファイルを読み込んだ時にデフォルト値を設定したい

背景

christina04.hatenablog.com

のように設定ファイルを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/yamlUnmarshalYAML() を実装することで内部処理をカスタマイズすることができます。
なのでその中で 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値が設定されています。

その他

サンプルコード

今回のサンプルコードはこちら

github.com

まとめ

GoでYAMLファイルを読み込んだ時にデフォルト値を設定する方法を紹介しました。

参考