Skip to content

Commit

Permalink
Parsing from map feature added
Browse files Browse the repository at this point in the history
  • Loading branch information
Asliddin Azizovich committed Oct 9, 2021
1 parent 2e4c0d6 commit 6ff4981
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 20 deletions.
59 changes: 43 additions & 16 deletions env.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func New(envFiles ...string) (Config, error) {
}, nil
}

func (e Config) Parse(s interface{}) error {
func (e Config) Parse(s interface{}, data map[string]string) error {
reflectValue := reflect.ValueOf(s)
if reflectValue.Kind() != reflect.Ptr || reflectValue.IsNil() {
return ErrNoPtr
Expand All @@ -152,34 +152,61 @@ func (e Config) Parse(s interface{}) error {
}

iFace := valueField.Addr().Interface()
if err := e.Parse(iFace); err != nil {
if err := e.Parse(iFace, data); err != nil {
return err
}
}

typeField := t.Field(i)
key, defaultValue := typeField.Tag.Get(e.EnvTag), typeField.Tag.Get(e.DefaultValueTag)
isRequired := typeField.Tag.Get(e.RequiredValueTag) == "true"
parsedValue, err := e.parseValue(typeField, data)

if key != "" {
value := e.getOrDefault(key, defaultValue)
if err != nil {
return err
}

parser := BuiltInParsers[typeField.Type.Kind()]
if parsedValue != nil {
reflectValue.Field(i).Set(reflect.ValueOf(parsedValue))
}
}

parsedValue, err := parser(value)
if err != nil {
return err
}
return nil
}

if parsedValue == "" && isRequired {
return fmt.Errorf("%s required", key)
}
func (e Config) parseValue(typeField reflect.StructField, data map[string]string) (interface{}, error) {
key, defaultValue := typeField.Tag.Get(e.EnvTag), typeField.Tag.Get(e.DefaultValueTag)
isRequired := typeField.Tag.Get(e.RequiredValueTag) == "true"

reflectValue.Field(i).Set(reflect.ValueOf(parsedValue))
if key != "" {
var value string
if data != nil {
value = e.getOrDefaultMap(key, defaultValue, data)
} else {
value = e.getOrDefault(key, defaultValue)
}

parser := BuiltInParsers[typeField.Type.Kind()]

parsedValue, err := parser(value)
if err != nil {
return nil, err
}

if parsedValue == "" && isRequired {
return nil, fmt.Errorf("%s required", key)
}

return parsedValue, nil
}

return nil
return nil, nil
}

func (e Config) getOrDefaultMap(key string, defaultValue string, data map[string]string) string {
if v, ok := data[key]; ok {
return string(v)
}

return defaultValue
}

func (e Config) getOrDefault(key string, defaultValue string) string {
Expand Down
78 changes: 74 additions & 4 deletions env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,11 @@ func TestParse(t *testing.T) {

conf := "config"

if err := env.Parse(conf); err == nil {
if err := env.Parse(conf, nil); err == nil {
t.Errorf("error parsing env to struct without pointer\n")
}

if err := env.Parse(&conf); err == nil {
if err := env.Parse(&conf, nil); err == nil {
t.Errorf("error parsing env to struct\n")
}

Expand All @@ -378,7 +378,7 @@ func TestParse(t *testing.T) {
InvalidValue int64 `env:"INVALID_VALUE" default:"1"`
}{}

if err := env.Parse(&cfg); err == nil {
if err := env.Parse(&cfg, nil); err == nil {
t.Errorf("error parsing env to struct: %v\n", err)
}

Expand All @@ -398,7 +398,77 @@ func TestParse(t *testing.T) {
URL string `env:"URL" required:"true" default:"https://google.com"`
}{}

if err := env.Parse(&config); err != nil {
if err := env.Parse(&config, nil); err != nil {
t.Errorf("error parsing env to struct: %v\n", err)
}

if config.App.LogLevel != "error" {
t.Errorf("error parsing nested struct value\n")
}

if config.DBName != "postgres" {
t.Errorf("error parsing with default value: %v\n", config.DBName)
}

if config.Port != 8081 {
t.Errorf("error parsing int64: %v\n", config.Port)
}

if config.FeePercent != float32(3.3) {
t.Errorf("error parsing float32\n")
}
}

func TestParseMap(t *testing.T) {
TestNew(t)

env, _ := New()

conf := "config"

if err := env.Parse(conf, nil); err == nil {
t.Errorf("error parsing env to struct without pointer\n")
}

if err := env.Parse(&conf, nil); err == nil {
t.Errorf("error parsing env to struct\n")
}

// with invalid value
// value in the .env file
// INVALID_VALUE=true
cfg := struct {
InvalidValue int64 `env:"INVALID_VALUE" default:"1"`
}{}

if err := env.Parse(&cfg, map[string]string{
"INVALID_VALUE": "true",
}); err == nil {
t.Errorf("error parsing env to struct: %v\n", err)
}

// values in the .env file
// LOG_LEVEL=error
// DB_NAME not set
// PORT=8081
// FEE_PERCENT=3.3
// INVALID_VALUE=true
config := struct {
App struct {
LogLevel string `env:"LOG_LEVEL" default:"debug"`
}
DBName string `env:"DB_NAME" default:"postgres"`
Port int64 `env:"PORT" default:"8080"`
FeePercent float32 `env:"FEE_PERCENT" default:"1"`
URL string `env:"URL" required:"true" default:"https://google.com"`
}{}

if err := env.Parse(&config, map[string]string{
"LOG_LEVEL": "error",
"PORT": "8081",
"FEE_PERCENT": "3.3",
"INVALID_VALUE": "true",
}); err != nil {
t.Errorf("error parsing env to struct: %v\n", err)
}

Expand Down

0 comments on commit 6ff4981

Please sign in to comment.