-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathfile.go
129 lines (117 loc) · 2.89 KB
/
file.go
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
package goconf
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
)
type fileLoader struct {
log func(string)
data reflect.Value
infoLog func(string)
errLog func(string)
}
func genTemplate(opts interface{}, fn string) error {
file, err := os.Create(fn)
defer file.Close()
if err != nil {
return err
}
ext := strings.ToLower(filepath.Ext(fn))
if encoder, ok := encodeFuncMap[ext]; ok {
ret, err := encoder(opts)
if err != nil {
return err
}
file.WriteString(ret)
return nil
}
return fmt.Errorf("file format not supported: " + filepath.Ext(fn))
}
func (c *fileLoader) Data() map[string]interface{} {
if c.data.IsValid() {
return c.data.Interface().(map[string]interface{})
}
return nil
}
func (c *fileLoader) Load(files []string) (err error) {
c.data, err = c._LoadFiles(c.data, files, true)
return
}
func (c *fileLoader) _LoadFiles(rdata reflect.Value, files []string, asc bool) (reflect.Value, error) {
var tmp reflect.Value
for _, file := range files {
data, err := c._LoadFile(file)
if err != nil {
return rdata, err
}
c.log(fmt.Sprintf("load: %s", file))
tmp = merge(tmp, reflect.ValueOf(data))
}
if asc {
rdata = merge(rdata, tmp)
} else {
rdata = merge(tmp, rdata)
}
return rdata, nil
}
func (c *fileLoader) _LoadFile(file string) (interface{}, error) {
bytes, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
ext := strings.ToLower(filepath.Ext(file))
var unmarshal decodeFunc
var ok bool
if unmarshal, ok = decodeFuncMap[ext]; !ok {
return nil, fmt.Errorf("file format not supported: %s %s", file, filepath.Ext(file))
}
var data interface{}
var inherit interface{}
if err = unmarshal(bytes, &data); err == nil {
dm := data.(map[string]interface{})
if inherit, ok = dm["inherit_files"]; !ok {
return data, err
}
var ret reflect.Value
basePath := filepath.Dir(file) + "/"
switch inherit.(type) {
case string:
name := basePath + inherit.(string)
ret, err = c._LoadFiles(reflect.ValueOf(data), []string{name}, false)
return ret.Interface(), err
case []interface{}:
var files []string
for _, fi := range inherit.([]interface{}) {
files = append(files, basePath+fi.(string))
}
ret, err = c._LoadFiles(reflect.ValueOf(data), files, false)
return ret.Interface(), err
}
return data, err
}
return nil, fmt.Errorf("load %s with error %s", file, err.Error())
}
func mapIndex(mp reflect.Value, index reflect.Value) reflect.Value {
v := mp.MapIndex(index)
if v.Kind() == reflect.Interface {
v = v.Elem()
}
return v
}
func merge(v1, v2 reflect.Value) reflect.Value {
if v1.Kind() != reflect.Map || v2.Kind() != reflect.Map || !v1.IsValid() {
return v2
}
for _, key := range v2.MapKeys() {
e1 := mapIndex(v1, key)
e2 := mapIndex(v2, key)
if e1.Kind() == reflect.Map && e2.Kind() == reflect.Map {
e2 = merge(e1, e2)
}
v1.SetMapIndex(key, e2)
}
return v1
}