diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/go.mod b/go.mod index a68b5cd..8b8f830 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,6 @@ go 1.14 require ( git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349 github.com/BurntSushi/toml v0.3.1 + github.com/otakukaze/envconfig v1.0.4 gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index a50e755..313e2e2 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349 h1:V6ifeiJ3ExnjaUylTO git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349/go.mod h1:yE+qbsUsijCTdwsaQRkPT1CXYk7ftMzXsCaaYx/0QI0= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/otakukaze/envconfig v1.0.4 h1:/rZ8xq1vFpgWzqsqUkk61doDGNv9pIXqrog/mCvSx8Y= +github.com/otakukaze/envconfig v1.0.4/go.mod h1:v2dNv5NX1Lakw3FTAkbxYURyaiOy68M8QpMTZz+ogfs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= diff --git a/loader.go b/loader.go index 9deffd0..7a6639a 100644 --- a/loader.go +++ b/loader.go @@ -9,6 +9,7 @@ import ( "git.trj.tw/golang/utils" "github.com/BurntSushi/toml" + "github.com/otakukaze/envconfig" "gopkg.in/yaml.v2" ) @@ -27,6 +28,7 @@ type ConfigFile struct { type LoadOptions struct { ConfigFile *ConfigFile + FromEnv bool } func Load(i interface{}, opts *LoadOptions) error { @@ -50,48 +52,51 @@ func Load(i interface{}, opts *LoadOptions) error { return nil } - // no config file - if opts.ConfigFile == nil { - return nil - } + // load config file + if opts.ConfigFile != nil { + if opts.ConfigFile.Path == "" { + return errors.New("config file path empty") + } - if opts.ConfigFile.Path == "" { - return errors.New("config file path empty") - } + // resolve file path + opts.ConfigFile.Path = utils.ParsePath(opts.ConfigFile.Path) + // check file exists + if !utils.CheckExists(opts.ConfigFile.Path, false) { + return errors.New("config file not found") + } - // resolve file path - opts.ConfigFile.Path = utils.ParsePath(opts.ConfigFile.Path) - // check file exists - if !utils.CheckExists(opts.ConfigFile.Path, false) { - return errors.New("config file not found") - } - - filebyte, err := ioutil.ReadFile(opts.ConfigFile.Path) - if err != nil { - return err - } - - switch opts.ConfigFile.Type { - case ConfigFileTypeJSON: - err := json.Unmarshal(filebyte, i) + filebyte, err := ioutil.ReadFile(opts.ConfigFile.Path) if err != nil { return err } - break - case ConfigFileTypeTOML: - err := toml.Unmarshal(filebyte, i) - if err != nil { - return err + + switch opts.ConfigFile.Type { + case ConfigFileTypeJSON: + err := json.Unmarshal(filebyte, i) + if err != nil { + return err + } + break + case ConfigFileTypeTOML: + err := toml.Unmarshal(filebyte, i) + if err != nil { + return err + } + break + case ConfigFileTypeYAML: + err := yaml.Unmarshal(filebyte, i) + if err != nil { + return err + } + break + default: + return errors.New("file type not impl") } - break - case ConfigFileTypeYAML: - err := yaml.Unmarshal(filebyte, i) - if err != nil { - return err - } - break - default: - return errors.New("file type not impl") + } + + // load config from env + if opts.FromEnv { + envconfig.Parse(i) } return nil diff --git a/loader_test.go b/loader_test.go index 9430508..35da841 100644 --- a/loader_test.go +++ b/loader_test.go @@ -1,6 +1,7 @@ package confloader import ( + "os" "reflect" "testing" ) @@ -13,8 +14,8 @@ func TestLoad(t *testing.T) { Key string `json:"key" yaml:"key" toml:"key"` } type Config struct { - StrKey string `json:"strKey" yaml:"strKey" toml:"strKey" default:"def value"` - IntKey int `json:"intKey" yaml:"intKey" toml:"intKey"` + StrKey string `json:"strKey" yaml:"strKey" toml:"strKey" default:"def value" env:"TEST_STR"` + IntKey int `json:"intKey" yaml:"intKey" toml:"intKey" env:"TEST_INT"` BoolKey bool `json:"boolKey" yaml:"boolKey" toml:"boolKey"` FloatKey float64 `json:"floatKey" yaml:"floatKey" toml:"floatKey"` StrArr []string `json:"strArr" yaml:"strArr" toml:"strArr" default:"arrval" length:"1"` @@ -23,6 +24,9 @@ func TestLoad(t *testing.T) { ArrObj []ArrObj `json:"arrObj" yaml:"arrObj" toml:"arrObj"` } + os.Setenv("TEST_STR", "string value2") + os.Setenv("TEST_INT", "2000") + expected := Config{ StrKey: "string value", IntKey: 1000, @@ -35,8 +39,20 @@ func TestLoad(t *testing.T) { }, ArrObj: []ArrObj{{Key: "val"}}, } + expectedWithEnv := Config{ + StrKey: "string value2", + IntKey: 2000, + BoolKey: true, + FloatKey: 1.2345, + StrArr: []string{"arr1"}, + StrArr2: []string{"arrval2", "arrval2"}, + ObjKey: ObjKey{ + Name: "name", + }, + ArrObj: []ArrObj{{Key: "val"}}, + } - src := Config{} + // src := Config{} type args struct { i interface{} @@ -51,7 +67,6 @@ func TestLoad(t *testing.T) { { name: "test load json file with default", args: args{ - i: &src, opts: &LoadOptions{ ConfigFile: &ConfigFile{ Type: ConfigFileTypeJSON, @@ -64,7 +79,6 @@ func TestLoad(t *testing.T) { { name: "test load yaml file with default", args: args{ - i: &src, opts: &LoadOptions{ ConfigFile: &ConfigFile{ Type: ConfigFileTypeYAML, @@ -77,7 +91,6 @@ func TestLoad(t *testing.T) { { name: "test load toml file with default", args: args{ - i: &src, opts: &LoadOptions{ ConfigFile: &ConfigFile{ Type: ConfigFileTypeTOML, @@ -87,14 +100,35 @@ func TestLoad(t *testing.T) { }, wantErr: false, }, + { + name: "test load json file with default and env", + args: args{ + opts: &LoadOptions{ + ConfigFile: &ConfigFile{ + Type: ConfigFileTypeJSON, + Path: "./test/config.json", + }, + FromEnv: true, + }, + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + src := Config{} + tt.args.i = &src if err := Load(tt.args.i, tt.args.opts); (err != nil) != tt.wantErr { t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr) } - if !reflect.DeepEqual(src, expected) { - t.Errorf("Load and expected not match") + if tt.args.opts != nil && tt.args.opts.FromEnv { + if !reflect.DeepEqual(src, expectedWithEnv) { + t.Errorf("Load and expected not match") + } + } else { + if !reflect.DeepEqual(src, expected) { + t.Errorf("Load and expected not match") + } } }) }