0
0

More than 1 year has passed since last update.

koanfの罠:空文字をスライスにUnmarshalしても空スライスにならない

Posted at

はじめに

koanf で環境変数を読み込む際、ProviderWithValue を使うことでスライスに設定を流し込むことができます。

readmeより一部改変
// 設定オブジェクト。環境変数を読み込んでここにセット
type config struct {
	Vals []string
}

func main() {
	k := koanf.New(".")

	k.Load(env.ProviderWithValue("MYVAR_", ".", func(s string, v string) (string, interface{}) {
		key := strings.Replace(strings.ToLower(strings.TrimPrefix(s, "MYVAR_")), "_", ".", -1)

		// 値にスペースがあれば、値をスペース区切りでスライス化
		if strings.Contains(v, " ") {
			return key, strings.Split(v, " ")
		}

		// それ以外の場合は、文字列をそのまま返す
		return key, v
	}), nil)

	c := config{}
	k.Unmarshal("", &c)

	fmt.Printf("%#v\n", c)
}
環境変数
$ export MYVAR_VALS="foo bar baz"
セットされたconfigの中身
main.config{Vals:[]string{"foo", "bar", "baz"}}

しかし、この環境変数に空文字を指定すると...
空スライス []string{} ではなく、空文字要素を含むスライス []string{""} にUnmarshalされてしまいます!

環境変数
$ export MYVAR_VALS=
セットされたconfigの中身
main.config{Vals:[]string{""}}

要素無指定にしたつもりが空文字を指定してしまっているので要注意!無事ハマりました

バージョン

  • koanf v1.4.2

why?

バグではなく、依存パッケージ mapstructure の仕様によるものでした。

Koanf.Unmarshal は内部的に mapstructure.Decoder.Decode を呼んでいます。

そして、Decode

  • 変換元のデータが文字列

かつ

  • 変換先フィールドがスライス

の場合に、暗黙的にスライスに変換(lift) してくれます。

この機能によって、環境変数に1要素(foo)しか指定しなくても、自動でスライスに変換して([]string{"foo"})セットしてくれます。

...そして、環境変数が空文字の場合は 「空文字1要素」が「空文字を含むスライス」( []string{""} )になってしまったというわけです。

対処方法

愚直に、Unmarshal後のconfigを変換して対処することになりそうです(オプション等があれば楽にできそうですが...)。
幸い []string{""} の場合だけ考えればよいのでそこまで複雑な変換ロジックは不要でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0