| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512 | // Copyright 2014 Unknwon//// 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 iniimport (	"bytes"	"errors"	"fmt"	"reflect"	"strings"	"time"	"unicode")// NameMapper represents a ini tag name mapper.type NameMapper func(string) string// Built-in name getters.var (	// AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.	AllCapsUnderscore NameMapper = func(raw string) string {		newstr := make([]rune, 0, len(raw))		for i, chr := range raw {			if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {				if i > 0 {					newstr = append(newstr, '_')				}			}			newstr = append(newstr, unicode.ToUpper(chr))		}		return string(newstr)	}	// TitleUnderscore converts to format title_underscore.	TitleUnderscore NameMapper = func(raw string) string {		newstr := make([]rune, 0, len(raw))		for i, chr := range raw {			if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {				if i > 0 {					newstr = append(newstr, '_')				}				chr -= ('A' - 'a')			}			newstr = append(newstr, chr)		}		return string(newstr)	})func (s *Section) parseFieldName(raw, actual string) string {	if len(actual) > 0 {		return actual	}	if s.f.NameMapper != nil {		return s.f.NameMapper(raw)	}	return raw}func parseDelim(actual string) string {	if len(actual) > 0 {		return actual	}	return ","}var reflectTime = reflect.TypeOf(time.Now()).Kind()// setSliceWithProperType sets proper values to slice based on its type.func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {	var strs []string	if allowShadow {		strs = key.StringsWithShadows(delim)	} else {		strs = key.Strings(delim)	}	numVals := len(strs)	if numVals == 0 {		return nil	}	var vals interface{}	var err error	sliceOf := field.Type().Elem().Kind()	switch sliceOf {	case reflect.String:		vals = strs	case reflect.Int:		vals, err = key.parseInts(strs, true, false)	case reflect.Int64:		vals, err = key.parseInt64s(strs, true, false)	case reflect.Uint:		vals, err = key.parseUints(strs, true, false)	case reflect.Uint64:		vals, err = key.parseUint64s(strs, true, false)	case reflect.Float64:		vals, err = key.parseFloat64s(strs, true, false)	case reflectTime:		vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)	default:		return fmt.Errorf("unsupported type '[]%s'", sliceOf)	}	if err != nil && isStrict {		return err	}	slice := reflect.MakeSlice(field.Type(), numVals, numVals)	for i := 0; i < numVals; i++ {		switch sliceOf {		case reflect.String:			slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i]))		case reflect.Int:			slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i]))		case reflect.Int64:			slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i]))		case reflect.Uint:			slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i]))		case reflect.Uint64:			slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i]))		case reflect.Float64:			slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i]))		case reflectTime:			slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i]))		}	}	field.Set(slice)	return nil}func wrapStrictError(err error, isStrict bool) error {	if isStrict {		return err	}	return nil}// setWithProperType sets proper value to field based on its type,// but it does not return error for failing parsing,// because we want to use default value that is already assigned to strcut.func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {	switch t.Kind() {	case reflect.String:		if len(key.String()) == 0 {			return nil		}		field.SetString(key.String())	case reflect.Bool:		boolVal, err := key.Bool()		if err != nil {			return wrapStrictError(err, isStrict)		}		field.SetBool(boolVal)	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:		durationVal, err := key.Duration()		// Skip zero value		if err == nil && int64(durationVal) > 0 {			field.Set(reflect.ValueOf(durationVal))			return nil		}		intVal, err := key.Int64()		if err != nil {			return wrapStrictError(err, isStrict)		}		field.SetInt(intVal)	//	byte is an alias for uint8, so supporting uint8 breaks support for byte	case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:		durationVal, err := key.Duration()		// Skip zero value		if err == nil && int(durationVal) > 0 {			field.Set(reflect.ValueOf(durationVal))			return nil		}		uintVal, err := key.Uint64()		if err != nil {			return wrapStrictError(err, isStrict)		}		field.SetUint(uintVal)	case reflect.Float32, reflect.Float64:		floatVal, err := key.Float64()		if err != nil {			return wrapStrictError(err, isStrict)		}		field.SetFloat(floatVal)	case reflectTime:		timeVal, err := key.Time()		if err != nil {			return wrapStrictError(err, isStrict)		}		field.Set(reflect.ValueOf(timeVal))	case reflect.Slice:		return setSliceWithProperType(key, field, delim, allowShadow, isStrict)	default:		return fmt.Errorf("unsupported type '%s'", t)	}	return nil}func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) {	opts := strings.SplitN(tag, ",", 3)	rawName = opts[0]	if len(opts) > 1 {		omitEmpty = opts[1] == "omitempty"	}	if len(opts) > 2 {		allowShadow = opts[2] == "allowshadow"	}	return rawName, omitEmpty, allowShadow}func (s *Section) mapTo(val reflect.Value, isStrict bool) error {	if val.Kind() == reflect.Ptr {		val = val.Elem()	}	typ := val.Type()	for i := 0; i < typ.NumField(); i++ {		field := val.Field(i)		tpField := typ.Field(i)		tag := tpField.Tag.Get("ini")		if tag == "-" {			continue		}		rawName, _, allowShadow := parseTagOptions(tag)		fieldName := s.parseFieldName(tpField.Name, rawName)		if len(fieldName) == 0 || !field.CanSet() {			continue		}		isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous		isStruct := tpField.Type.Kind() == reflect.Struct		if isAnonymous {			field.Set(reflect.New(tpField.Type.Elem()))		}		if isAnonymous || isStruct {			if sec, err := s.f.GetSection(fieldName); err == nil {				if err = sec.mapTo(field, isStrict); err != nil {					return fmt.Errorf("error mapping field(%s): %v", fieldName, err)				}				continue			}		}		if key, err := s.GetKey(fieldName); err == nil {			delim := parseDelim(tpField.Tag.Get("delim"))			if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {				return fmt.Errorf("error mapping field(%s): %v", fieldName, err)			}		}	}	return nil}// MapTo maps section to given struct.func (s *Section) MapTo(v interface{}) error {	typ := reflect.TypeOf(v)	val := reflect.ValueOf(v)	if typ.Kind() == reflect.Ptr {		typ = typ.Elem()		val = val.Elem()	} else {		return errors.New("cannot map to non-pointer struct")	}	return s.mapTo(val, false)}// MapTo maps section to given struct in strict mode,// which returns all possible error including value parsing error.func (s *Section) StrictMapTo(v interface{}) error {	typ := reflect.TypeOf(v)	val := reflect.ValueOf(v)	if typ.Kind() == reflect.Ptr {		typ = typ.Elem()		val = val.Elem()	} else {		return errors.New("cannot map to non-pointer struct")	}	return s.mapTo(val, true)}// MapTo maps file to given struct.func (f *File) MapTo(v interface{}) error {	return f.Section("").MapTo(v)}// MapTo maps file to given struct in strict mode,// which returns all possible error including value parsing error.func (f *File) StrictMapTo(v interface{}) error {	return f.Section("").StrictMapTo(v)}// MapTo maps data sources to given struct with name mapper.func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {	cfg, err := Load(source, others...)	if err != nil {		return err	}	cfg.NameMapper = mapper	return cfg.MapTo(v)}// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode,// which returns all possible error including value parsing error.func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {	cfg, err := Load(source, others...)	if err != nil {		return err	}	cfg.NameMapper = mapper	return cfg.StrictMapTo(v)}// MapTo maps data sources to given struct.func MapTo(v, source interface{}, others ...interface{}) error {	return MapToWithMapper(v, nil, source, others...)}// StrictMapTo maps data sources to given struct in strict mode,// which returns all possible error including value parsing error.func StrictMapTo(v, source interface{}, others ...interface{}) error {	return StrictMapToWithMapper(v, nil, source, others...)}// reflectSliceWithProperType does the opposite thing as setSliceWithProperType.func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error {	slice := field.Slice(0, field.Len())	if field.Len() == 0 {		return nil	}	var buf bytes.Buffer	sliceOf := field.Type().Elem().Kind()	for i := 0; i < field.Len(); i++ {		switch sliceOf {		case reflect.String:			buf.WriteString(slice.Index(i).String())		case reflect.Int, reflect.Int64:			buf.WriteString(fmt.Sprint(slice.Index(i).Int()))		case reflect.Uint, reflect.Uint64:			buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))		case reflect.Float64:			buf.WriteString(fmt.Sprint(slice.Index(i).Float()))		case reflectTime:			buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))		default:			return fmt.Errorf("unsupported type '[]%s'", sliceOf)		}		buf.WriteString(delim)	}	key.SetValue(buf.String()[:buf.Len()-1])	return nil}// reflectWithProperType does the opposite thing as setWithProperType.func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {	switch t.Kind() {	case reflect.String:		key.SetValue(field.String())	case reflect.Bool:		key.SetValue(fmt.Sprint(field.Bool()))	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:		key.SetValue(fmt.Sprint(field.Int()))	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:		key.SetValue(fmt.Sprint(field.Uint()))	case reflect.Float32, reflect.Float64:		key.SetValue(fmt.Sprint(field.Float()))	case reflectTime:		key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))	case reflect.Slice:		return reflectSliceWithProperType(key, field, delim)	default:		return fmt.Errorf("unsupported type '%s'", t)	}	return nil}// CR: copied from encoding/json/encode.go with modifications of time.Time support.// TODO: add more test coverage.func isEmptyValue(v reflect.Value) bool {	switch v.Kind() {	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:		return v.Len() == 0	case reflect.Bool:		return !v.Bool()	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:		return v.Int() == 0	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:		return v.Uint() == 0	case reflect.Float32, reflect.Float64:		return v.Float() == 0	case reflect.Interface, reflect.Ptr:		return v.IsNil()	case reflectTime:		t, ok := v.Interface().(time.Time)		return ok && t.IsZero()	}	return false}func (s *Section) reflectFrom(val reflect.Value) error {	if val.Kind() == reflect.Ptr {		val = val.Elem()	}	typ := val.Type()	for i := 0; i < typ.NumField(); i++ {		field := val.Field(i)		tpField := typ.Field(i)		tag := tpField.Tag.Get("ini")		if tag == "-" {			continue		}		opts := strings.SplitN(tag, ",", 2)		if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) {			continue		}		fieldName := s.parseFieldName(tpField.Name, opts[0])		if len(fieldName) == 0 || !field.CanSet() {			continue		}		if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) ||			(tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {			// Note: The only error here is section doesn't exist.			sec, err := s.f.GetSection(fieldName)			if err != nil {				// Note: fieldName can never be empty here, ignore error.				sec, _ = s.f.NewSection(fieldName)			}			// Add comment from comment tag			if len(sec.Comment) == 0 {				sec.Comment = tpField.Tag.Get("comment")			}			if err = sec.reflectFrom(field); err != nil {				return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)			}			continue		}		// Note: Same reason as secion.		key, err := s.GetKey(fieldName)		if err != nil {			key, _ = s.NewKey(fieldName, "")		}		// Add comment from comment tag		if len(key.Comment) == 0 {			key.Comment = tpField.Tag.Get("comment")		}		if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {			return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)		}	}	return nil}// ReflectFrom reflects secion from given struct.func (s *Section) ReflectFrom(v interface{}) error {	typ := reflect.TypeOf(v)	val := reflect.ValueOf(v)	if typ.Kind() == reflect.Ptr {		typ = typ.Elem()		val = val.Elem()	} else {		return errors.New("cannot reflect from non-pointer struct")	}	return s.reflectFrom(val)}// ReflectFrom reflects file from given struct.func (f *File) ReflectFrom(v interface{}) error {	return f.Section("").ReflectFrom(v)}// ReflectFrom reflects data sources from given struct with name mapper.func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error {	cfg.NameMapper = mapper	return cfg.ReflectFrom(v)}// ReflectFrom reflects data sources from given struct.func ReflectFrom(cfg *File, v interface{}) error {	return ReflectFromWithMapper(cfg, v, nil)}
 |