main
1package gcfg
2
3import (
4 "bytes"
5 "encoding"
6 "encoding/gob"
7 "fmt"
8 "math/big"
9 "reflect"
10 "strings"
11 "unicode"
12 "unicode/utf8"
13
14 "gopkg.in/warnings.v0"
15
16 "github.com/go-git/gcfg/types"
17)
18
19type tag struct {
20 ident string
21 intMode string
22}
23
24func newTag(ts string) tag {
25 t := tag{}
26 s := strings.Split(ts, ",")
27 t.ident = s[0]
28 for _, tse := range s[1:] {
29 if strings.HasPrefix(tse, "int=") {
30 t.intMode = tse[len("int="):]
31 }
32 }
33 return t
34}
35
36func fieldFold(v reflect.Value, name string) (reflect.Value, tag) {
37 var n string
38 r0, _ := utf8.DecodeRuneInString(name)
39 if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) {
40 n = "X"
41 }
42 n += strings.Replace(name, "-", "_", -1)
43 f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool {
44 if !v.FieldByName(fieldName).CanSet() {
45 return false
46 }
47 f, _ := v.Type().FieldByName(fieldName)
48 t := newTag(f.Tag.Get("gcfg"))
49 if t.ident != "" {
50 return strings.EqualFold(t.ident, name)
51 }
52 return strings.EqualFold(n, fieldName)
53 })
54 if !ok {
55 return reflect.Value{}, tag{}
56 }
57 return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg"))
58}
59
60type setter func(destp interface{}, blank bool, val string, t tag) error
61
62var errUnsupportedType = fmt.Errorf("unsupported type")
63var errBlankUnsupported = fmt.Errorf("blank value not supported for type")
64
65var setters = []setter{
66 typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
67}
68
69func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error {
70 dtu, ok := d.(encoding.TextUnmarshaler)
71 if !ok {
72 return errUnsupportedType
73 }
74 if blank {
75 return errBlankUnsupported
76 }
77 return dtu.UnmarshalText([]byte(val))
78}
79
80func boolSetter(d interface{}, blank bool, val string, t tag) error {
81 if blank {
82 reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true))
83 return nil
84 }
85 b, err := types.ParseBool(val)
86 if err == nil {
87 reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b))
88 }
89 return err
90}
91
92func intMode(mode string) types.IntMode {
93 var m types.IntMode
94 if strings.ContainsAny(mode, "dD") {
95 m |= types.Dec
96 }
97 if strings.ContainsAny(mode, "hH") {
98 m |= types.Hex
99 }
100 if strings.ContainsAny(mode, "oO") {
101 m |= types.Oct
102 }
103 return m
104}
105
106var typeModes = map[reflect.Type]types.IntMode{
107 reflect.TypeOf(int(0)): types.Dec | types.Hex,
108 reflect.TypeOf(int8(0)): types.Dec | types.Hex,
109 reflect.TypeOf(int16(0)): types.Dec | types.Hex,
110 reflect.TypeOf(int32(0)): types.Dec | types.Hex,
111 reflect.TypeOf(int64(0)): types.Dec | types.Hex,
112 reflect.TypeOf(uint(0)): types.Dec | types.Hex,
113 reflect.TypeOf(uint8(0)): types.Dec | types.Hex,
114 reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
115 reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
116 reflect.TypeOf(uint64(0)): types.Dec | types.Hex,
117 // use default mode (allow dec/hex/oct) for uintptr type
118 reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
119}
120
121func intModeDefault(t reflect.Type) types.IntMode {
122 m, ok := typeModes[t]
123 if !ok {
124 m = types.Dec | types.Hex | types.Oct
125 }
126 return m
127}
128
129func intSetter(d interface{}, blank bool, val string, t tag) error {
130 if blank {
131 return errBlankUnsupported
132 }
133 mode := intMode(t.intMode)
134 if mode == 0 {
135 mode = intModeDefault(reflect.TypeOf(d).Elem())
136 }
137 return types.ParseInt(d, val, mode)
138}
139
140func stringSetter(d interface{}, blank bool, val string, t tag) error {
141 if blank {
142 return errBlankUnsupported
143 }
144 dsp, ok := d.(*string)
145 if !ok {
146 return errUnsupportedType
147 }
148 *dsp = val
149 return nil
150}
151
152var kindSetters = map[reflect.Kind]setter{
153 reflect.String: stringSetter,
154 reflect.Bool: boolSetter,
155 reflect.Int: intSetter,
156 reflect.Int8: intSetter,
157 reflect.Int16: intSetter,
158 reflect.Int32: intSetter,
159 reflect.Int64: intSetter,
160 reflect.Uint: intSetter,
161 reflect.Uint8: intSetter,
162 reflect.Uint16: intSetter,
163 reflect.Uint32: intSetter,
164 reflect.Uint64: intSetter,
165 reflect.Uintptr: intSetter,
166}
167
168var typeSetters = map[reflect.Type]setter{
169 reflect.TypeOf(big.Int{}): intSetter,
170}
171
172func typeSetter(d interface{}, blank bool, val string, tt tag) error {
173 t := reflect.ValueOf(d).Type().Elem()
174 setter, ok := typeSetters[t]
175 if !ok {
176 return errUnsupportedType
177 }
178 return setter(d, blank, val, tt)
179}
180
181func kindSetter(d interface{}, blank bool, val string, tt tag) error {
182 k := reflect.ValueOf(d).Type().Elem().Kind()
183 setter, ok := kindSetters[k]
184 if !ok {
185 return errUnsupportedType
186 }
187 return setter(d, blank, val, tt)
188}
189
190func scanSetter(d interface{}, blank bool, val string, tt tag) error {
191 if blank {
192 return errBlankUnsupported
193 }
194 return types.ScanFully(d, val, 'v')
195}
196
197func newValue(c *warnings.Collector, sect string, vCfg reflect.Value,
198 vType reflect.Type) (reflect.Value, error) {
199 //
200 pv := reflect.New(vType)
201 dfltName := "default-" + sect
202 dfltField, _ := fieldFold(vCfg, dfltName)
203 var err error
204 if dfltField.IsValid() {
205 b := bytes.NewBuffer(nil)
206 ge := gob.NewEncoder(b)
207 if err = c.Collect(ge.EncodeValue(dfltField)); err != nil {
208 return pv, err
209 }
210 gd := gob.NewDecoder(bytes.NewReader(b.Bytes()))
211 if err = c.Collect(gd.DecodeValue(pv.Elem())); err != nil {
212 return pv, err
213 }
214 }
215 return pv, nil
216}
217
218func set(c *warnings.Collector, cfg interface{}, sect, sub, name string,
219 value string, blankValue bool, subsectPass bool) error {
220 //
221 vPCfg := reflect.ValueOf(cfg)
222 if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct {
223 panic(fmt.Errorf("config must be a pointer to a struct"))
224 }
225 vCfg := vPCfg.Elem()
226 vSect, _ := fieldFold(vCfg, sect)
227 if !vSect.IsValid() {
228 err := extraData{section: sect}
229 return c.Collect(err)
230 }
231 isSubsect := vSect.Kind() == reflect.Map
232 if subsectPass != isSubsect {
233 return nil
234 }
235 if isSubsect {
236 vst := vSect.Type()
237 if vst.Key().Kind() != reflect.String ||
238 vst.Elem().Kind() != reflect.Ptr ||
239 vst.Elem().Elem().Kind() != reflect.Struct {
240 panic(fmt.Errorf("map field for section must have string keys and "+
241 " pointer-to-struct values: section %q", sect))
242 }
243 if vSect.IsNil() {
244 vSect.Set(reflect.MakeMap(vst))
245 }
246 k := reflect.ValueOf(sub)
247 pv := vSect.MapIndex(k)
248 if !pv.IsValid() {
249 vType := vSect.Type().Elem().Elem()
250 var err error
251 if pv, err = newValue(c, sect, vCfg, vType); err != nil {
252 return err
253 }
254 vSect.SetMapIndex(k, pv)
255 }
256 vSect = pv.Elem()
257 } else if vSect.Kind() != reflect.Struct {
258 panic(fmt.Errorf("field for section must be a map or a struct: "+
259 "section %q", sect))
260 } else if sub != "" {
261 err := extraData{section: sect, subsection: &sub}
262 return c.Collect(err)
263 }
264 // Empty name is a special value, meaning that only the
265 // section/subsection object is to be created, with no values set.
266 if name == "" {
267 return nil
268 }
269 vVar, t := fieldFold(vSect, name)
270 if !vVar.IsValid() {
271 var err error
272 if isSubsect {
273 err = extraData{section: sect, subsection: &sub, variable: &name}
274 } else {
275 err = extraData{section: sect, variable: &name}
276 }
277 return c.Collect(err)
278 }
279 // vVal is either single-valued var, or newly allocated value within multi-valued var
280 var vVal reflect.Value
281 // multi-value if unnamed slice type
282 isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice ||
283 vVar.Type().Name() == "" && vVar.Kind() == reflect.Ptr && vVar.Type().Elem().Name() == "" && vVar.Type().Elem().Kind() == reflect.Slice
284 if isMulti && vVar.Kind() == reflect.Ptr {
285 if vVar.IsNil() {
286 vVar.Set(reflect.New(vVar.Type().Elem()))
287 }
288 vVar = vVar.Elem()
289 }
290 if isMulti && blankValue {
291 vVar.Set(reflect.Zero(vVar.Type()))
292 return nil
293 }
294 if isMulti {
295 vVal = reflect.New(vVar.Type().Elem()).Elem()
296 } else {
297 vVal = vVar
298 }
299 isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr
300 isNew := isDeref && vVal.IsNil()
301 // vAddr is address of value to set (dereferenced & allocated as needed)
302 var vAddr reflect.Value
303 switch {
304 case isNew:
305 vAddr = reflect.New(vVal.Type().Elem())
306 case isDeref && !isNew:
307 vAddr = vVal
308 default:
309 vAddr = vVal.Addr()
310 }
311 vAddrI := vAddr.Interface()
312 err, ok := error(nil), false
313 for _, s := range setters {
314 err = s(vAddrI, blankValue, value, t)
315 if err == nil {
316 ok = true
317 break
318 }
319 if err != errUnsupportedType {
320 return err
321 }
322 }
323 if !ok {
324 // in case all setters returned errUnsupportedType
325 return err
326 }
327 if isNew { // set reference if it was dereferenced and newly allocated
328 vVal.Set(vAddr)
329 }
330 if isMulti { // append if multi-valued
331 vVar.Set(reflect.Append(vVar, vVal))
332 }
333 return nil
334}