aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPacien TRAN-GIRARD2015-02-14 11:24:17 +0100
committerPacien TRAN-GIRARD2015-02-14 11:27:36 +0100
commit8e99baedf2c3e433750c2f51681f3c9d15d412be (patch)
tree3fa0ff3ea4823a408dadceb14fcd51ddc16c2aa8
parent8b1f88ce72ef8496879395e7f04b676e6a16e07a (diff)
downloadgo-envcfg-8e99baedf2c3e433750c2f51681f3c9d15d412be.tar.gz
First version
-rw-r--r--envcfg.go40
-rw-r--r--mapper.go86
-rw-r--r--nesting_test.go52
-rw-r--r--overwriting_test.go67
-rw-r--r--tagging_test.go70
-rw-r--r--typeconv_test.go71
6 files changed, 386 insertions, 0 deletions
diff --git a/envcfg.go b/envcfg.go
new file mode 100644
index 0000000..b91ea13
--- /dev/null
+++ b/envcfg.go
@@ -0,0 +1,40 @@
1// Package envcfg provides environment variable mapping to structs.
2//
3// Can be used to read configuration parameters from the environment.
4//
5// Fields for which environment variables can be found are overwritten, otherwise they are left to their previous
6// value.
7//
8// Can be used, for example, after gcfg to override settings provided in a configuration file.
9package envcfg
10
11import (
12 "errors"
13 "reflect"
14)
15
16const (
17 TAG = "env"
18 ABS_TAG = "absenv"
19 SEP = "_"
20)
21
22type node struct {
23 parent *node
24 value *reflect.Value
25 properties *reflect.StructField
26}
27
28var ErrInvalidConfigStruct = errors.New("invalid parameter: must map to a struct")
29
30func ReadInto(cfgStruct interface{}) (interface{}, []error) {
31 s := reflect.ValueOf(cfgStruct).Elem()
32
33 if s.Kind() != reflect.Struct {
34 return nil, []error{ErrInvalidConfigStruct}
35 }
36
37 _, errs := setStructFields(node{nil, &s, nil})
38
39 return cfgStruct, errs
40}
diff --git a/mapper.go b/mapper.go
new file mode 100644
index 0000000..ca6885b
--- /dev/null
+++ b/mapper.go
@@ -0,0 +1,86 @@
1package envcfg
2
3import (
4 "os"
5 "reflect"
6 "strconv"
7 "strings"
8)
9
10func getEnvName(n node) string {
11 name := n.properties.Tag.Get(TAG)
12 if name == "" {
13 name = n.properties.Name
14 }
15
16 abs, _ := strconv.ParseBool(n.properties.Tag.Get(ABS_TAG))
17 if !abs && n.parent != nil {
18 if n.parent.properties != nil {
19 parentName := getEnvName(*n.parent)
20 name = parentName + SEP + name
21 }
22 }
23
24 return strings.ToUpper(name)
25}
26
27func setValue(n node, v string) (node, error) {
28 switch n.value.Kind() {
29 case reflect.String:
30 n.value.SetString(v)
31
32 case reflect.Bool:
33 boolVal, err := strconv.ParseBool(v)
34 if err != nil {
35 return n, err
36 }
37 n.value.SetBool(boolVal)
38
39 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
40 intVal, err := strconv.ParseInt(v, 0, n.value.Type().Bits())
41 if err != nil {
42 return n, err
43 }
44 n.value.SetInt(intVal)
45
46 case reflect.Float32, reflect.Float64:
47 floatVal, err := strconv.ParseFloat(v, n.value.Type().Bits())
48 if err != nil {
49 return n, err
50 }
51 n.value.SetFloat(floatVal)
52 }
53
54 return n, nil
55}
56
57func setFieldValue(n node) (node, error) {
58 if n.value.Kind() == reflect.Struct {
59 setStructFields(n)
60 return n, nil
61 }
62
63 v := os.Getenv(getEnvName(n))
64 if v != "" {
65 return setValue(n, v)
66 }
67
68 return n, nil
69}
70
71func setStructFields(n node) (node, []error) {
72 t := n.value.Type()
73 errs := []error{}
74
75 for i := 0; i < n.value.NumField(); i++ {
76 v := n.value.Field(i)
77 p := t.Field(i)
78
79 _, err := setFieldValue(node{&n, &v, &p})
80 if err != nil {
81 errs = append(errs, err)
82 }
83 }
84
85 return n, errs
86}
diff --git a/nesting_test.go b/nesting_test.go
new file mode 100644
index 0000000..3fcc059
--- /dev/null
+++ b/nesting_test.go
@@ -0,0 +1,52 @@
1package envcfg
2
3import (
4 "os"
5 "testing"
6)
7
8type nestedStruct struct {
9 SubStruct struct {
10 Field string
11 }
12}
13
14func TestNestedMapping(t *testing.T) {
15 const ENV_KEY = "SUBSTRUCT_FIELD"
16 const ENV_VAL = "Remember: testing is the future!"
17
18 os.Clearenv()
19 os.Setenv(ENV_KEY, ENV_VAL)
20
21 s := nestedStruct{}
22
23 ReadInto(&s)
24
25 if s.SubStruct.Field != ENV_VAL {
26 t.Errorf("expected '%s', got '%s'", ENV_VAL, s.SubStruct.Field)
27 }
28}
29
30type deeplyNestedStruct struct {
31 SubStruct struct {
32 SubSubStruct struct {
33 Field string
34 }
35 }
36}
37
38func TestDeeplyNestedMapping(t *testing.T) {
39 const ENV_KEY = "SUBSTRUCT_SUBSUBSTRUCT_FIELD"
40 const ENV_VAL = "Remember: testing is the future!"
41
42 os.Clearenv()
43 os.Setenv(ENV_KEY, ENV_VAL)
44
45 s := deeplyNestedStruct{}
46
47 ReadInto(&s)
48
49 if s.SubStruct.SubSubStruct.Field != ENV_VAL {
50 t.Errorf("expected '%s', got '%s'", ENV_VAL, s.SubStruct.SubSubStruct.Field)
51 }
52}
diff --git a/overwriting_test.go b/overwriting_test.go
new file mode 100644
index 0000000..1ed5daa
--- /dev/null
+++ b/overwriting_test.go
@@ -0,0 +1,67 @@
1package envcfg
2
3import (
4 "os"
5 "testing"
6)
7
8func TestKeeping(t *testing.T) {
9 const ORIG_VAL = "Remember: testing is the future!"
10
11 os.Clearenv()
12
13 s := struct{ Field string }{ORIG_VAL}
14
15 ReadInto(&s)
16
17 if s.Field != ORIG_VAL {
18 t.Errorf("expected '%s', got '%s'", ORIG_VAL, s.Field)
19 }
20}
21
22func TestOverwriting(t *testing.T) {
23 const ENV_KEY = "FIELD"
24 const ENV_VAL = "Remember: testing is the future!"
25 const ORIG_VAL = "Testing is pointless!"
26
27 os.Clearenv()
28 os.Setenv(ENV_KEY, ENV_VAL)
29
30 s := struct{ Field string }{ORIG_VAL}
31
32 ReadInto(&s)
33
34 if s.Field != ENV_VAL {
35 t.Errorf("expected '%s', got '%s'", ENV_VAL, s.Field)
36 }
37}
38
39type superStruct struct {
40 SubStruct nestedFields
41}
42
43type nestedFields struct {
44 KeepMe string
45 OverwriteMe string
46}
47
48func TestMultiOverwriting(t *testing.T) {
49 const ENV_KEY = "SUBSTRUCT_OVERWRITEME"
50 const ENV_VAL = "Remember: testing is the future!"
51 const ORIG_VAL = "Testing is pointless!"
52
53 os.Clearenv()
54 os.Setenv(ENV_KEY, ENV_VAL)
55
56 s := superStruct{nestedFields{ORIG_VAL, ORIG_VAL}}
57
58 ReadInto(&s)
59
60 if s.SubStruct.KeepMe != ORIG_VAL {
61 t.Errorf("expected '%s', got '%s'", ORIG_VAL, s.SubStruct.KeepMe)
62 }
63
64 if s.SubStruct.OverwriteMe != ENV_VAL {
65 t.Errorf("expected '%s', got '%s'", ENV_VAL, s.SubStruct.OverwriteMe)
66 }
67}
diff --git a/tagging_test.go b/tagging_test.go
new file mode 100644
index 0000000..2fe2e33
--- /dev/null
+++ b/tagging_test.go
@@ -0,0 +1,70 @@
1package envcfg
2
3import (
4 "os"
5 "testing"