...

Source file src/edge-infra.dev/pkg/lib/ini/struct.go

Documentation: edge-infra.dev/pkg/lib/ini

     1  package ini
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  	"unicode"
    13  )
    14  
    15  // NameMapper represents a ini tag name mapper.
    16  type NameMapper func(string) string
    17  
    18  // Built-in name getters.
    19  var (
    20  	// SnackCase converts to format SNACK_CASE.
    21  	SnackCase NameMapper = func(raw string) string {
    22  		newstr := make([]rune, 0, len(raw))
    23  		for i, chr := range raw {
    24  			if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
    25  				if i > 0 {
    26  					newstr = append(newstr, '_')
    27  				}
    28  			}
    29  			newstr = append(newstr, unicode.ToUpper(chr))
    30  		}
    31  		return string(newstr)
    32  	}
    33  	// TitleUnderscore converts to format title_underscore.
    34  	TitleUnderscore NameMapper = func(raw string) string {
    35  		newstr := make([]rune, 0, len(raw))
    36  		for i, chr := range raw {
    37  			if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
    38  				if i > 0 {
    39  					newstr = append(newstr, '_')
    40  				}
    41  				chr -= 'A' - 'a'
    42  			}
    43  			newstr = append(newstr, chr)
    44  		}
    45  		return string(newstr)
    46  	}
    47  )
    48  
    49  func (s *Section) parseFieldName(raw, actual string) string {
    50  	if len(actual) > 0 {
    51  		return actual
    52  	}
    53  	if s.f.NameMapper != nil {
    54  		return s.f.NameMapper(raw)
    55  	}
    56  	return raw
    57  }
    58  
    59  func parseDelim(actual string) string {
    60  	if len(actual) > 0 {
    61  		return actual
    62  	}
    63  	return ","
    64  }
    65  
    66  var reflectTime = reflect.TypeOf(time.Now()).Kind()
    67  
    68  // setSliceWithProperType sets proper values to slice based on its type.
    69  func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
    70  	var strs []string
    71  	if allowShadow {
    72  		strs = key.StringsWithShadows(delim)
    73  	} else {
    74  		strs = key.Strings(delim)
    75  	}
    76  
    77  	numVals := len(strs)
    78  	if numVals == 0 {
    79  		return nil
    80  	}
    81  
    82  	var vals interface{}
    83  	var err error
    84  
    85  	sliceOf := field.Type().Elem().Kind()
    86  	switch sliceOf {
    87  	case reflect.String:
    88  		vals = strs
    89  	case reflect.Int:
    90  		vals, err = key.parseInts(strs, true, false)
    91  	case reflect.Int64:
    92  		vals, err = key.parseInt64s(strs, true, false)
    93  	case reflect.Uint:
    94  		vals, err = key.parseUints(strs, true, false)
    95  	case reflect.Uint64:
    96  		vals, err = key.parseUint64s(strs, true, false)
    97  	case reflect.Float64:
    98  		vals, err = key.parseFloat64s(strs, true, false)
    99  	case reflect.Bool:
   100  		vals, err = key.parseBools(strs, true, false)
   101  	case reflectTime:
   102  		vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)
   103  	default:
   104  		return fmt.Errorf("unsupported type '[]%s'", sliceOf)
   105  	}
   106  	if err != nil && isStrict {
   107  		return err
   108  	}
   109  
   110  	slice := reflect.MakeSlice(field.Type(), numVals, numVals)
   111  	for i := 0; i < numVals; i++ {
   112  		switch sliceOf {
   113  		case reflect.String:
   114  			slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i]))
   115  		case reflect.Int:
   116  			slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i]))
   117  		case reflect.Int64:
   118  			slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i]))
   119  		case reflect.Uint:
   120  			slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i]))
   121  		case reflect.Uint64:
   122  			slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i]))
   123  		case reflect.Float64:
   124  			slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i]))
   125  		case reflect.Bool:
   126  			slice.Index(i).Set(reflect.ValueOf(vals.([]bool)[i]))
   127  		case reflectTime:
   128  			slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i]))
   129  		}
   130  	}
   131  	field.Set(slice)
   132  	return nil
   133  }
   134  
   135  func wrapStrictError(err error, isStrict bool) error {
   136  	if isStrict {
   137  		return err
   138  	}
   139  	return nil
   140  }
   141  
   142  // setWithProperType sets proper value to field based on its type,
   143  // but it does not return error for failing parsing,
   144  // because we want to use default value that is already assigned to struct.
   145  func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
   146  	vt := t
   147  	isPtr := t.Kind() == reflect.Ptr
   148  	if isPtr {
   149  		vt = t.Elem()
   150  	}
   151  	switch vt.Kind() {
   152  	case reflect.String:
   153  		stringVal := key.String()
   154  		if isPtr {
   155  			field.Set(reflect.ValueOf(&stringVal))
   156  		} else if len(stringVal) > 0 {
   157  			field.SetString(key.String())
   158  		}
   159  	case reflect.Bool:
   160  		boolVal, err := key.Bool()
   161  		if err != nil {
   162  			return wrapStrictError(err, isStrict)
   163  		}
   164  		if isPtr {
   165  			field.Set(reflect.ValueOf(&boolVal))
   166  		} else {
   167  			field.SetBool(boolVal)
   168  		}
   169  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   170  		// ParseDuration will not return err for `0`, so check the type name
   171  		if vt.Name() == "Duration" {
   172  			durationVal, err := key.Duration()
   173  			if err != nil {
   174  				if intVal, err := key.Int64(); err == nil {
   175  					field.SetInt(intVal)
   176  					return nil
   177  				}
   178  				return wrapStrictError(err, isStrict)
   179  			}
   180  			if isPtr {
   181  				field.Set(reflect.ValueOf(&durationVal))
   182  			} else if int64(durationVal) > 0 {
   183  				field.Set(reflect.ValueOf(durationVal))
   184  			}
   185  			return nil
   186  		}
   187  
   188  		intVal, err := key.Int64()
   189  		if err != nil {
   190  			return wrapStrictError(err, isStrict)
   191  		}
   192  		if isPtr {
   193  			pv := reflect.New(t.Elem())
   194  			pv.Elem().SetInt(intVal)
   195  			field.Set(pv)
   196  		} else {
   197  			field.SetInt(intVal)
   198  		}
   199  	//	byte is an alias for uint8, so supporting uint8 breaks support for byte
   200  	case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   201  		durationVal, err := key.Duration()
   202  		// Skip zero value
   203  		if err == nil && uint64(durationVal) > 0 {
   204  			if isPtr {
   205  				field.Set(reflect.ValueOf(&durationVal))
   206  			} else {
   207  				field.Set(reflect.ValueOf(durationVal))
   208  			}
   209  			return nil
   210  		}
   211  
   212  		uintVal, err := key.Uint64()
   213  		if err != nil {
   214  			return wrapStrictError(err, isStrict)
   215  		}
   216  		if isPtr {
   217  			pv := reflect.New(t.Elem())
   218  			pv.Elem().SetUint(uintVal)
   219  			field.Set(pv)
   220  		} else {
   221  			field.SetUint(uintVal)
   222  		}
   223  
   224  	case reflect.Float32, reflect.Float64:
   225  		floatVal, err := key.Float64()
   226  		if err != nil {
   227  			return wrapStrictError(err, isStrict)
   228  		}
   229  		if isPtr {
   230  			pv := reflect.New(t.Elem())
   231  			pv.Elem().SetFloat(floatVal)
   232  			field.Set(pv)
   233  		} else {
   234  			field.SetFloat(floatVal)
   235  		}
   236  	case reflectTime:
   237  		timeVal, err := key.Time()
   238  		if err != nil {
   239  			return wrapStrictError(err, isStrict)
   240  		}
   241  		if isPtr {
   242  			field.Set(reflect.ValueOf(&timeVal))
   243  		} else {
   244  			field.Set(reflect.ValueOf(timeVal))
   245  		}
   246  	case reflect.Slice:
   247  		return setSliceWithProperType(key, field, delim, allowShadow, isStrict)
   248  	default:
   249  		return fmt.Errorf("unsupported type %q", t)
   250  	}
   251  	return nil
   252  }
   253  
   254  func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool, extends bool) {
   255  	opts := strings.SplitN(tag, ",", 5)
   256  	rawName = opts[0]
   257  	for _, opt := range opts[1:] {
   258  		omitEmpty = omitEmpty || (opt == "omitempty")
   259  		allowShadow = allowShadow || (opt == "allowshadow")
   260  		allowNonUnique = allowNonUnique || (opt == "nonunique")
   261  		extends = extends || (opt == "extends")
   262  	}
   263  	return rawName, omitEmpty, allowShadow, allowNonUnique, extends
   264  }
   265  
   266  // mapToField maps the given value to the matching field of the given section.
   267  // The sectionIndex is the index (if non unique sections are enabled) to which the value should be added.
   268  func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error {
   269  	if val.Kind() == reflect.Ptr {
   270  		val = val.Elem()
   271  	}
   272  	typ := val.Type()
   273  
   274  	for i := 0; i < typ.NumField(); i++ {
   275  		field := val.Field(i)
   276  		tpField := typ.Field(i)
   277  
   278  		tag := tpField.Tag.Get("ini")
   279  		if tag == "-" {
   280  			continue
   281  		}
   282  
   283  		rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
   284  		fieldName := s.parseFieldName(tpField.Name, rawName)
   285  		if len(fieldName) == 0 || !field.CanSet() {
   286  			continue
   287  		}
   288  
   289  		isStruct := tpField.Type.Kind() == reflect.Struct
   290  		isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct
   291  		isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
   292  		if isAnonymousPtr {
   293  			field.Set(reflect.New(tpField.Type.Elem()))
   294  		}
   295  
   296  		if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) {
   297  			if isStructPtr && field.IsNil() {
   298  				field.Set(reflect.New(tpField.Type.Elem()))
   299  			}
   300  			fieldSection := s
   301  			if rawName != "" {
   302  				sectionName = s.name + s.f.options.ChildSectionDelimiter + rawName
   303  				if secs, err := s.f.SectionsByName(sectionName); err == nil && sectionIndex < len(secs) {
   304  					fieldSection = secs[sectionIndex]
   305  				}
   306  			}
   307  			if err := fieldSection.mapToField(field, isStrict, sectionIndex, sectionName); err != nil {
   308  				return fmt.Errorf("map to field %q: %v", fieldName, err)
   309  			}
   310  		} else if isAnonymousPtr || isStruct || isStructPtr {
   311  			if secs, err := s.f.SectionsByName(fieldName); err == nil {
   312  				if len(secs) <= sectionIndex {
   313  					return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName)
   314  				}
   315  				// Only set the field to non-nil struct value if we have a section for it.
   316  				// Otherwise, we end up with a non-nil struct ptr even though there is no data.
   317  				if isStructPtr && field.IsNil() {
   318  					field.Set(reflect.New(tpField.Type.Elem()))
   319  				}
   320  				if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex, fieldName); err != nil {
   321  					return fmt.Errorf("map to field %q: %v", fieldName, err)
   322  				}
   323  				continue
   324  			}
   325  		}
   326  
   327  		// Map non-unique sections
   328  		if allowNonUnique && tpField.Type.Kind() == reflect.Slice {
   329  			newField, err := s.mapToSlice(fieldName, field, isStrict)
   330  			if err != nil {
   331  				return fmt.Errorf("map to slice %q: %v", fieldName, err)
   332  			}
   333  
   334  			field.Set(newField)
   335  			continue
   336  		}
   337  
   338  		if key, err := s.GetKey(fieldName); err == nil {
   339  			delim := parseDelim(tpField.Tag.Get("delim"))
   340  			if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
   341  				return fmt.Errorf("set field %q: %v", fieldName, err)
   342  			}
   343  		}
   344  	}
   345  	return nil
   346  }
   347  
   348  // mapToSlice maps all sections with the same name and returns the new value.
   349  // The type of the Value must be a slice.
   350  func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (reflect.Value, error) {
   351  	secs, err := s.f.SectionsByName(secName)
   352  	if err != nil {
   353  		return reflect.Value{}, err
   354  	}
   355  
   356  	typ := val.Type().Elem()
   357  	for i, sec := range secs {
   358  		elem := reflect.New(typ)
   359  		if err = sec.mapToField(elem, isStrict, i, sec.name); err != nil {
   360  			return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err)
   361  		}
   362  
   363  		val = reflect.Append(val, elem.Elem())
   364  	}
   365  	return val, nil
   366  }
   367  
   368  // mapTo maps a section to object v.
   369  func (s *Section) mapTo(v interface{}, isStrict bool) error {
   370  	typ := reflect.TypeOf(v)
   371  	val := reflect.ValueOf(v)
   372  	if typ.Kind() == reflect.Ptr {
   373  		typ = typ.Elem()
   374  		val = val.Elem()
   375  	} else {
   376  		return errors.New("not a pointer to a struct")
   377  	}
   378  
   379  	if typ.Kind() == reflect.Slice {
   380  		newField, err := s.mapToSlice(s.name, val, isStrict)
   381  		if err != nil {
   382  			return err
   383  		}
   384  
   385  		val.Set(newField)
   386  		return nil
   387  	}
   388  
   389  	return s.mapToField(val, isStrict, 0, s.name)
   390  }
   391  
   392  // MapTo maps section to given struct.
   393  func (s *Section) MapTo(v interface{}) error {
   394  	return s.mapTo(v, false)
   395  }
   396  
   397  // StrictMapTo maps section to given struct in strict mode,
   398  // which returns all possible error including value parsing error.
   399  func (s *Section) StrictMapTo(v interface{}) error {
   400  	return s.mapTo(v, true)
   401  }
   402  
   403  // MapTo maps file to given struct.
   404  func (f *File) MapTo(v interface{}) error {
   405  	return f.Section("").MapTo(v)
   406  }
   407  
   408  // StrictMapTo maps file to given struct in strict mode,
   409  // which returns all possible error including value parsing error.
   410  func (f *File) StrictMapTo(v interface{}) error {
   411  	return f.Section("").StrictMapTo(v)
   412  }
   413  
   414  // MapToWithMapper maps data sources to given struct with name mapper.
   415  func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
   416  	cfg, err := Load(source, others...)
   417  	if err != nil {
   418  		return err
   419  	}
   420  	cfg.NameMapper = mapper
   421  	return cfg.MapTo(v)
   422  }
   423  
   424  // StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode,
   425  // which returns all possible error including value parsing error.
   426  func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
   427  	cfg, err := Load(source, others...)
   428  	if err != nil {
   429  		return err
   430  	}
   431  	cfg.NameMapper = mapper
   432  	return cfg.StrictMapTo(v)
   433  }
   434  
   435  // MapTo maps data sources to given struct.
   436  func MapTo(v, source interface{}, others ...interface{}) error {
   437  	return MapToWithMapper(v, nil, source, others...)
   438  }
   439  
   440  // StrictMapTo maps data sources to given struct in strict mode,
   441  // which returns all possible error including value parsing error.
   442  func StrictMapTo(v, source interface{}, others ...interface{}) error {
   443  	return StrictMapToWithMapper(v, nil, source, others...)
   444  }
   445  
   446  // reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
   447  func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error {
   448  	slice := field.Slice(0, field.Len())
   449  	if field.Len() == 0 {
   450  		return nil
   451  	}
   452  	sliceOf := field.Type().Elem().Kind()
   453  
   454  	if allowShadow {
   455  		var keyWithShadows *Key
   456  		for i := 0; i < field.Len(); i++ {
   457  			var val string
   458  			switch sliceOf {
   459  			case reflect.String:
   460  				val = slice.Index(i).String()
   461  			case reflect.Int, reflect.Int64:
   462  				val = fmt.Sprint(slice.Index(i).Int())
   463  			case reflect.Uint, reflect.Uint64:
   464  				val = fmt.Sprint(slice.Index(i).Uint())
   465  			case reflect.Float64:
   466  				val = fmt.Sprint(slice.Index(i).Float())
   467  			case reflect.Bool:
   468  				val = fmt.Sprint(slice.Index(i).Bool())
   469  			case reflectTime:
   470  				val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339)
   471  			default:
   472  				return fmt.Errorf("unsupported type '[]%s'", sliceOf)
   473  			}
   474  
   475  			if i == 0 {
   476  				keyWithShadows = newKey(key.s, key.name, val)
   477  			} else {
   478  				_ = keyWithShadows.AddShadow(val)
   479  			}
   480  		}
   481  		*key = *keyWithShadows
   482  		return nil
   483  	}
   484  
   485  	var buf bytes.Buffer
   486  	for i := 0; i < field.Len(); i++ {
   487  		switch sliceOf {
   488  		case reflect.String:
   489  			buf.WriteString(slice.Index(i).String())
   490  		case reflect.Int, reflect.Int64:
   491  			buf.WriteString(fmt.Sprint(slice.Index(i).Int()))
   492  		case reflect.Uint, reflect.Uint64:
   493  			buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))
   494  		case reflect.Float64:
   495  			buf.WriteString(fmt.Sprint(slice.Index(i).Float()))
   496  		case reflect.Bool:
   497  			buf.WriteString(fmt.Sprint(slice.Index(i).Bool()))
   498  		case reflectTime:
   499  			buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))
   500  		default:
   501  			return fmt.Errorf("unsupported type '[]%s'", sliceOf)
   502  		}
   503  		buf.WriteString(delim)
   504  	}
   505  	key.SetValue(buf.String()[:buf.Len()-len(delim)])
   506  	return nil
   507  }
   508  
   509  // reflectWithProperType does the opposite thing as setWithProperType.
   510  func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error {
   511  	switch t.Kind() {
   512  	case reflect.String:
   513  		key.SetValue(field.String())
   514  	case reflect.Bool:
   515  		key.SetValue(fmt.Sprint(field.Bool()))
   516  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   517  		key.SetValue(fmt.Sprint(field.Int()))
   518  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   519  		key.SetValue(fmt.Sprint(field.Uint()))
   520  	case reflect.Float32, reflect.Float64:
   521  		key.SetValue(strconv.FormatFloat(field.Float(), 'f', -1, 64))
   522  	case reflectTime:
   523  		key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))
   524  	case reflect.Slice:
   525  		return reflectSliceWithProperType(key, field, delim, allowShadow)
   526  	case reflect.Ptr:
   527  		if !field.IsNil() {
   528  			return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow)
   529  		}
   530  	default:
   531  		return fmt.Errorf("unsupported type %q", t)
   532  	}
   533  	return nil
   534  }
   535  
   536  // CR: copied from encoding/json/encode.go with modifications of time.Time support.
   537  // TODO: add more test coverage.
   538  func isEmptyValue(v reflect.Value) bool {
   539  	switch v.Kind() {
   540  	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
   541  		return v.Len() == 0
   542  	case reflect.Bool:
   543  		return !v.Bool()
   544  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   545  		return v.Int() == 0
   546  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   547  		return v.Uint() == 0
   548  	case reflect.Float32, reflect.Float64:
   549  		return v.Float() == 0
   550  	case reflect.Interface, reflect.Ptr:
   551  		return v.IsNil()
   552  	case reflectTime:
   553  		t, ok := v.Interface().(time.Time)
   554  		return ok && t.IsZero()
   555  	}
   556  	return false
   557  }
   558  
   559  // StructReflector is the interface implemented by struct types that can extract themselves into INI objects.
   560  type StructReflector interface {
   561  	ReflectINIStruct(*File) error
   562  }
   563  
   564  func (s *Section) reflectFrom(val reflect.Value) error {
   565  	if val.Kind() == reflect.Ptr {
   566  		val = val.Elem()
   567  	}
   568  
   569  	if val.Kind() == reflect.Map {
   570  		return s.reflectFromMap(val)
   571  	}
   572  
   573  	typ := val.Type()
   574  
   575  	for i := 0; i < typ.NumField(); i++ {
   576  		if !val.Field(i).CanInterface() {
   577  			continue
   578  		}
   579  
   580  		field := val.Field(i)
   581  		tpField := typ.Field(i)
   582  
   583  		tag := tpField.Tag.Get("ini")
   584  		if tag == "-" {
   585  			continue
   586  		}
   587  
   588  		rawName, omitEmpty, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
   589  		if omitEmpty && isEmptyValue(field) {
   590  			continue
   591  		}
   592  
   593  		if r, ok := field.Interface().(StructReflector); ok {
   594  			return r.ReflectINIStruct(s.f)
   595  		}
   596  
   597  		fieldName := s.parseFieldName(tpField.Name, rawName)
   598  		if len(fieldName) == 0 || !field.CanSet() {
   599  			continue
   600  		}
   601  
   602  		if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Ptr || tpField.Type.Kind() == reflect.Struct) {
   603  			if err := s.reflectFrom(field); err != nil {
   604  				return fmt.Errorf("reflect from field %q: %v", fieldName, err)
   605  			}
   606  			continue
   607  		}
   608  
   609  		if (tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct) ||
   610  			(tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {
   611  			// Note: The only error here is section doesn't exist.
   612  			sec, err := s.f.GetSection(fieldName)
   613  			if err != nil {
   614  				// Note: fieldName can never be empty here, ignore error.
   615  				sec, _ = s.f.NewSection(fieldName)
   616  			}
   617  
   618  			// Add comment from comment tag
   619  			if len(sec.Comment) == 0 {
   620  				sec.Comment = tpField.Tag.Get("comment")
   621  			}
   622  
   623  			if err = sec.reflectFrom(field); err != nil {
   624  				return fmt.Errorf("reflect from field %q: %v", fieldName, err)
   625  			}
   626  			continue
   627  		}
   628  
   629  		if allowNonUnique && tpField.Type.Kind() == reflect.Slice {
   630  			slice := field.Slice(0, field.Len())
   631  			if field.Len() == 0 {
   632  				return nil
   633  			}
   634  			sliceOf := field.Type().Elem().Kind()
   635  
   636  			for i := 0; i < field.Len(); i++ {
   637  				if sliceOf != reflect.Struct && sliceOf != reflect.Ptr {
   638  					return fmt.Errorf("field %q is not a slice of pointer or struct", fieldName)
   639  				}
   640  
   641  				sec, err := s.f.NewSection(fieldName)
   642  				if err != nil {
   643  					return err
   644  				}
   645  
   646  				// Add comment from comment tag
   647  				if len(sec.Comment) == 0 {
   648  					sec.Comment = tpField.Tag.Get("comment")
   649  				}
   650  
   651  				if err := sec.reflectFrom(slice.Index(i)); err != nil {
   652  					return fmt.Errorf("reflect from field %q: %v", fieldName, err)
   653  				}
   654  			}
   655  			continue
   656  		}
   657  
   658  		// Note: Same reason as section.
   659  		key, err := s.GetKey(fieldName)
   660  		if err != nil {
   661  			key, _ = s.NewKey(fieldName, "")
   662  		}
   663  
   664  		// Add comment from comment tag
   665  		if len(key.Comment) == 0 {
   666  			key.Comment = tpField.Tag.Get("comment")
   667  		}
   668  
   669  		delim := parseDelim(tpField.Tag.Get("delim"))
   670  		if err = reflectWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil {
   671  			return fmt.Errorf("reflect field %q: %v", fieldName, err)
   672  		}
   673  
   674  	}
   675  	return nil
   676  }
   677  
   678  // reflectFromMap support maps with keys of type strings
   679  // issues open at https://github.com/go-ini/ini/issues?q=is%3Aissue+is%3Aopen+map
   680  func (s *Section) reflectFromMap(val reflect.Value) error {
   681  	keys := val.MapKeys()
   682  	// make predictable output
   683  	sort.Slice(keys, func(i, j int) bool { return keys[i].String() < keys[j].String() })
   684  	for _, k := range keys {
   685  		if k.Kind() != reflect.String {
   686  			return fmt.Errorf("invalid key for map, only strings allowed. key %s", k.Kind())
   687  		}
   688  		fieldName := k.String()
   689  		key, err := s.GetKey(fieldName)
   690  		if err != nil {
   691  			key, _ = s.NewKey(fieldName, "")
   692  		}
   693  		v := val.MapIndex(k)
   694  		if err = reflectWithProperType(v.Type(), key, v, ",", false); err != nil {
   695  			return fmt.Errorf("reflect field %q: %v", fieldName, err)
   696  		}
   697  	}
   698  	return nil
   699  }
   700  
   701  // ReflectFrom reflects section from given struct. It overwrites existing ones.
   702  func (s *Section) ReflectFrom(v interface{}) error {
   703  	typ := reflect.TypeOf(v)
   704  	val := reflect.ValueOf(v)
   705  
   706  	if s.name != DefaultSection && s.f.options.AllowNonUniqueSections &&
   707  		(typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr) {
   708  		// Clear sections to make sure none exists before adding the new ones
   709  		s.f.DeleteSection(s.name)
   710  
   711  		if typ.Kind() == reflect.Ptr {
   712  			sec, err := s.f.NewSection(s.name)
   713  			if err != nil {
   714  				return err
   715  			}
   716  			return sec.reflectFrom(val.Elem())
   717  		}
   718  
   719  		slice := val.Slice(0, val.Len())
   720  		sliceOf := val.Type().Elem().Kind()
   721  		if sliceOf != reflect.Ptr {
   722  			return fmt.Errorf("not a slice of pointers")
   723  		}
   724  
   725  		for i := 0; i < slice.Len(); i++ {
   726  			sec, err := s.f.NewSection(s.name)
   727  			if err != nil {
   728  				return err
   729  			}
   730  
   731  			err = sec.reflectFrom(slice.Index(i))
   732  			if err != nil {
   733  				return fmt.Errorf("reflect from %dth field: %v", i, err)
   734  			}
   735  		}
   736  
   737  		return nil
   738  	}
   739  
   740  	if typ.Kind() == reflect.Ptr {
   741  		val = val.Elem()
   742  	} else if typ.Kind() != reflect.Map {
   743  		return errors.New("not a pointer to a struct")
   744  	}
   745  
   746  	return s.reflectFrom(val)
   747  }
   748  
   749  // ReflectFrom reflects file from given struct.
   750  func (f *File) ReflectFrom(v interface{}) error {
   751  	return f.Section("").ReflectFrom(v)
   752  }
   753  
   754  // ReflectFromWithMapper reflects data sources from given struct with name mapper.
   755  func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error {
   756  	cfg.NameMapper = mapper
   757  	return cfg.ReflectFrom(v)
   758  }
   759  
   760  // ReflectFrom reflects data sources from given struct.
   761  func ReflectFrom(cfg *File, v interface{}) error {
   762  	return ReflectFromWithMapper(cfg, v, nil)
   763  }
   764  

View as plain text