This repository was archived by the owner on Dec 28, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathappconfig.go
More file actions
133 lines (124 loc) · 3.4 KB
/
appconfig.go
File metadata and controls
133 lines (124 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (c) 2013 Dmitry Motylev. All rights reserved.
// Use of this source code is governed by the MIT License that can be found in
// the LICENSE file.
package appconfig
import (
"fmt"
"reflect"
"strconv"
"time"
)
// Source provides values for the fields
type Source interface {
// Err returns the first error that was encountered by the source
Err() error
// Lookup returns string form value for the field and false if value ws not found
Lookup(reflect.StructField) (string, bool)
}
// Error describes why it was impossible to assign a value to the struct's field.
type Error struct {
typ string
fld string
kind string
val string
cause error
}
func (e *Error) Error() string {
return fmt.Sprintf("can't set %s.%s (%s) to '%s': %v",
e.typ, e.fld, e.kind, e.val, e.cause)
}
// Load populates given struct from ordered list of sources.
// First "positive" lookup will be used as a value for the field.
func Load(v interface{}, s ...Source) error {
if reflect.Indirect(reflect.ValueOf(v)).Kind() != reflect.Struct {
panic("scan for non-struct type")
}
if len(s) == 0 {
return nil
}
scn := &scanner{s}
return scn.scan(v)
}
type scanner struct {
sources []Source
}
func (s *scanner) lookup(f reflect.StructField) (string, bool) {
for _, src := range s.sources {
if v, found := src.Lookup(f); found {
return v, true
}
}
return "", false
}
func (s *scanner) scan(v interface{}) error {
for _, src := range s.sources {
if err := src.Err(); err != nil {
return err
}
}
vStruct := reflect.ValueOf(v).Elem()
for i := 0; i < vStruct.NumField(); i++ {
f := vStruct.Field(i)
if !f.CanSet() {
continue
}
fStruct := vStruct.Type().Field(i)
vString, found := s.lookup(fStruct)
if !found {
vString = fStruct.Tag.Get("default")
if vString == "" {
continue
}
}
switch f.Kind() {
case reflect.String:
f.SetString(vString)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
vUint, err := strconv.ParseUint(vString, 10, f.Type().Bits())
if err != nil {
return &Error{vStruct.Type().Name(), fStruct.Name, f.Kind().String(), vString, err}
}
f.SetUint(vUint)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var vInt int64
var err error
if f.Type().Name() == "Duration" { // time.Duration
var d time.Duration
d, err = time.ParseDuration(vString)
vInt = int64(d)
} else {
vInt, err = strconv.ParseInt(vString, 10, f.Type().Bits())
}
if err != nil {
return &Error{vStruct.Type().Name(), fStruct.Name, f.Kind().String(), vString, err}
}
f.SetInt(vInt)
case reflect.Float32, reflect.Float64:
vFloat, err := strconv.ParseFloat(vString, f.Type().Bits())
if err != nil {
return &Error{vStruct.Type().Name(), fStruct.Name, f.Kind().String(), vString, err}
}
f.SetFloat(vFloat)
case reflect.Bool:
vBool, err := strconv.ParseBool(vString)
if err != nil {
return &Error{vStruct.Type().Name(), fStruct.Name, f.Kind().String(), vString, err}
}
f.SetBool(vBool)
case reflect.Struct:
if f.Type().Name() != "Time" { // time.Time
continue
}
format := fStruct.Tag.Get("time,format")
if format == "" {
format = time.UnixDate
}
d, err := time.Parse(format, vString)
if err != nil {
return &Error{vStruct.Type().Name(), fStruct.Name, f.Kind().String(), vString, err}
}
f.Set(reflect.ValueOf(d))
}
}
return nil
}