main
Raw Download raw file
  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}