現在Goでyamlで書かれた設定ファイルを読み込むロジックを作成しています。

gopkg.in/yaml.v2を使ってyamlファイルを読み込むと、map[interface{}]interface{}が得られそれをmitchellh/mapstructureでそれぞれのapp固有のconfig構造体に変換しています。

コードは以下のようになっています。

baseapp.go

package common

import (
    "gopkg.in/yaml.v2"
    "io/ioutil"
)

// BaseApp is base of app.
type BaseApp struct {
}

// LoadConfig loads config
func (ba *BaseApp) LoadConfig(fpath string, config Config) (err error) {
    b, err := ioutil.ReadFile(fpath)
    if err != nil {
        return
    }
    var c interface{}
    if err = yaml.Unmarshal(b, &c); err != nil {
        return
    }
    return config.ConvertMap(c)
}

config.go

package common

type Config interface {
    ConvertMap(input interface{}) error
}

これらの使い方は以下のテストのようになっています。

package common

import (
    "testing"

    "github.com/mitchellh/mapstructure"
)

type SampleConfig struct {
    Name    string   `yaml:"name"`
    Number  int      `yaml:"number"`
    Strings []string `yaml:"strings"`
}

//これは共通処理なので構造体ごとに実装したくない。(sc *SampleConfigがかわるだけ)
func (sc *SampleConfig) ConvertMap(input interface{}) error {
    return mapstructure.Decode(input, &sc)
}

type SampleApp struct {
    *BaseApp
}

func TestLoadConfig(t *testing.T) {
    sampleApp := &SampleApp{}
    var sampleConfig SampleConfig
    err := sampleApp.LoadConfig("testdata/sample_config.yml", &sampleConfig)
    if err != nil {
        t.Fatal(err)
    }

    if sampleConfig.Name != "test name" {
        t.Errorf("Name is not same as %s: %s", "test name", sampleConfig.Name)
    }

    if sampleConfig.Number != 100 {
        t.Errorf("Number is not same as %d: %d", 100, sampleConfig.Number)
    }
}

しかしこれでは、それぞれのconfigこのテストの例ではSampleConfigごとにConvertMapの処理を実装しなければなりません。またConvertMapの実装自体はそれぞれの構造体で同じになってしまいます。

これは無駄なのでどうにかしてそれぞれのconfigConvertMapを実装する必要をなくしたいのですが、そのようなことは可能でしょうか。