Skip to content
Permalink
Browse files

Initial commit

  • Loading branch information...
Depado committed Nov 7, 2017
1 parent 07225e8 commit 44640c2bc5af35886cf0350ea67d1f33501ba058
Showing with 189 additions and 0 deletions.
  1. +121 −0 conftags.go
  2. +68 −0 conftags_test.go
@@ -0,0 +1,121 @@
package conftags

import (
"fmt"
"os"
"reflect"
"strconv"
"time"

"github.com/sirupsen/logrus"
)

const envtag = "env"
const defaulttag = "default"

func set(field reflect.Value, refType reflect.StructField, value string) error {
switch field.Kind() {
case reflect.String:
field.SetString(value)
case reflect.Bool:
bvalue, err := strconv.ParseBool(value)
if err != nil {
return err
}
field.SetBool(bvalue)
case reflect.Int:
intValue, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
field.SetInt(intValue)
case reflect.Uint:
uintValue, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return err
}
field.SetUint(uintValue)
case reflect.Float32:
v, err := strconv.ParseFloat(value, 32)
if err != nil {
return err
}
field.SetFloat(v)
case reflect.Float64:
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
field.Set(reflect.ValueOf(v))
case reflect.Int64:
if refType.Type.String() == "time.Duration" {
dValue, err := time.ParseDuration(value)
if err != nil {
return err
}
field.Set(reflect.ValueOf(dValue))
} else {
intValue, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
field.SetInt(intValue)
}
}
return nil
}

func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Func, reflect.Map, reflect.Slice:
return v.IsNil()
case reflect.Array:
z := true
for i := 0; i < v.Len(); i++ {
z = z && isZero(v.Index(i))
}
return z
case reflect.Struct:
z := true
for i := 0; i < v.NumField(); i++ {
z = z && isZero(v.Field(i))
}
return z
}
z := reflect.Zero(v.Type())
return v.Interface() == z.Interface()
}

// Parse parses the function
func Parse(in interface{}) error {
ref := reflect.ValueOf(in).Elem()
refType := ref.Type()

// Iterate over the struct fields
for i := 0; i < refType.NumField(); i++ {
f := ref.Field(i)
ff := refType.Field(i)

// Check for environment
if e := ff.Tag.Get(envtag); e != "" {
if p, ok := os.LookupEnv(e); ok {
if err := set(f, ff, p); err != nil {
return err
}
}
}

// Check for default
if d := ff.Tag.Get(defaulttag); d != "" && isZero(f) {
logrus.WithFields(logrus.Fields{
"field": fmt.Sprintf("%s.%s", refType.Name(), ff.Name),
"default": d,
}).Warn("No configured value, using default")
if err := set(f, ff, d); err != nil {
return err
}
}
}

return nil
}
@@ -0,0 +1,68 @@
package conftags

import (
"os"
"reflect"
"testing"

"github.com/alecthomas/assert"
)

func TestParse(t *testing.T) {
var err error
type Mystruct struct {
Myfield int `env:"MYFIELD" default:"10"`
}
in := Mystruct{}
// Test the fallback to the default field tag
err = Parse(&in)
assert.NoError(t, err, "error should be nil")
assert.Equal(t, in.Myfield, 10)

// Test the environment variable has an impact on the struct
os.Setenv("MYFIELD", "11")
err = Parse(&in)
assert.NoError(t, err, "error should be nil")
assert.Equal(t, in.Myfield, 11)

// Test that an error is returned when the type is not compatible
os.Setenv("MYFIELD", "random")
err = Parse(&in)
assert.Error(t, err)

type erroneous struct {
Myfield int `default:"x"`
}
// Test the fallback to the default field tag
err = Parse(&erroneous{})
assert.Error(t, err, "should throw back an error")
}

func Test_isZero(t *testing.T) {
var nilslice []string
var nilmap map[string]string
type args struct {
v reflect.Value
}
tests := []struct {
name string
args reflect.Value
want bool
}{
{"nil slice should return true", reflect.ValueOf(nilslice), true},
{"nil map should return true", reflect.ValueOf(nilmap), true},
{"slice should be false", reflect.ValueOf([]string{}), false},
{"map should be false", reflect.ValueOf(new(map[bool]bool)), false},
{"struct instance should be false", reflect.ValueOf(struct{ One bool }{true}), false},
{"empty struct should be true", reflect.ValueOf(struct{ One bool }{}), true},
{"empty array should be true", reflect.ValueOf([0]string{}), true},
{"array should be false", reflect.ValueOf([1]string{"hi"}), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isZero(tt.args); got != tt.want {
t.Errorf("isZero() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 44640c2

Please sign in to comment.
You can’t perform that action at this time.