-
Notifications
You must be signed in to change notification settings - Fork 1
/
source.go
161 lines (136 loc) · 4.75 KB
/
source.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2019 xgfone
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gconf
import (
"fmt"
"time"
)
var errNoDecoder = fmt.Errorf("no decoder")
// SourceError represents an error about the data source.
type SourceError struct {
Source string
Format string
Data []byte
Err error
}
func (se SourceError) Error() string {
return fmt.Sprintf("source(%s)[%s]: %s", se.Source, se.Format, se.Err.Error())
}
// NewSourceError returns a new source error.
func NewSourceError(source, format string, data []byte, err error) SourceError {
return SourceError{Source: source, Format: format, Data: data, Err: err}
}
// DataSet represents the information of the configuration data.
type DataSet struct {
Data []byte // The original data.
Format string // Such as "json", "xml", etc.
Source string // Such as "file:/path/to/file", "zk:127.0.0.1:2181", etc.
Checksum string // Such as "md5:7d2f31e6fff478337478413ee1b70d2a", etc.
Timestamp time.Time // The timestamp when the data is modified.
// Callback will be called after loading the dataset, if it exists.
Callback func(DataSet)
}
// Md5 returns the md5 checksum of the DataSet data
func (ds DataSet) Md5() string {
return bytesToMd5(ds.Data)
}
// Sha256 returns the sha256 checksum of the DataSet data
func (ds DataSet) Sha256() string {
return bytesToSha256(ds.Data)
}
// LoadDataSet loads the DataSet ds, which will parse the data by calling the
// corresponding decoder and load it.
//
// If a certain option has been set, it will be ignored. But you can set force
// to true to reset the value of this option.
func (c *Config) LoadDataSet(ds DataSet, force ...bool) {
if len(ds.Data) == 0 {
return
}
decoder, ok := c.GetDecoder(ds.Format)
if !ok {
c.handleError(NewSourceError(ds.Source, ds.Format, ds.Data, errNoDecoder))
return
}
ms := make(map[string]interface{}, 32)
if err := decoder.Decode(ds.Data, ms); err != nil {
c.handleError(NewSourceError(ds.Source, ds.Format, ds.Data, err))
} else {
c.LoadMap(ms, force...)
}
if ds.Callback != nil {
ds.Callback(ds)
}
}
func (c *Config) loadDataSetWithError(ds DataSet, err error, force bool) {
switch err.(type) {
case nil:
c.LoadDataSet(ds, force)
case SourceError:
c.handleError(err)
default:
c.handleError(NewSourceError(ds.Source, ds.Format, ds.Data, err))
}
}
func (c *Config) loadDataSetCallback(ds DataSet, err error) {
c.loadDataSetWithError(ds, err, true)
}
// Source represents a data source where the data is.
type Source interface {
// Read reads the source data once, which should not block.
Read() (DataSet, error)
// Watch watches the change of the source, then send the changed data to ds.
//
// The source can check whether close is closed to determine whether the
// configuration is closed and to do any cleanup.
Watch(load func(DataSet, error), close <-chan struct{})
}
// LoadSource loads the sources, and call Watch to watch the source.
//
// When loading the source, if a certain option of a certain group has been set,
// it will be ignored. But you can set force to true to reset the value of this
// option.
func (c *Config) LoadSource(source Source, force ...bool) {
c.loadSource(source, true, nil, force...)
}
// LoadSourceWithoutWatch is the same as LoadSource, but does not call
// the Watch method of the source to watch the source.
func (c *Config) LoadSourceWithoutWatch(source Source, force ...bool) {
c.loadSource(source, false, nil, force...)
}
// LoadSourceAndCallback is the same as LoadSource, but set the callback
// of the Dataset to cb.
func (c *Config) LoadSourceAndCallback(source Source, cb func(DataSet), force ...bool) {
c.loadSource(source, true, cb, force...)
}
// LoadSourceAndCallbackWithoutWatch is the same as LoadSourceWithoutWatch,
// but set the callback of the Dataset to cb.
func (c *Config) LoadSourceAndCallbackWithoutWatch(source Source, cb func(DataSet), force ...bool) {
c.loadSource(source, false, cb, force...)
}
func (c *Config) loadSource(source Source, watch bool, cb func(DataSet), force ...bool) {
if source == nil {
panic("the source is nil")
}
var _force bool
if len(force) > 0 {
_force = force[0]
}
ds, err := source.Read()
ds.Callback = cb
c.loadDataSetWithError(ds, err, _force)
if watch {
source.Watch(c.loadDataSetCallback, c.exit)
}
}