diff options
author | Tamal Saha <tamal@appscode.com> | 2019-08-23 09:40:30 -0700 |
---|---|---|
committer | techknowlogick <techknowlogick@gitea.io> | 2019-08-23 12:40:29 -0400 |
commit | 171b3598778a1ecd0a921c71ed6755bfef68f7f0 (patch) | |
tree | 02857629ef9e8e26ee0ee559153f803f77b588b7 /vendor/gopkg.in/ini.v1 | |
parent | ca6fb004ac50fc924861112403895d637c6a2d1d (diff) | |
download | gitea-171b3598778a1ecd0a921c71ed6755bfef68f7f0.tar.gz gitea-171b3598778a1ecd0a921c71ed6755bfef68f7f0.zip |
Use gitea forked macaron (#7933)
Signed-off-by: Tamal Saha <tamal@appscode.com>
Diffstat (limited to 'vendor/gopkg.in/ini.v1')
-rw-r--r-- | vendor/gopkg.in/ini.v1/.travis.yml | 2 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/README.md | 20 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/error.go | 2 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/file.go | 12 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/ini.go | 32 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/key.go | 11 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/parser.go | 135 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/section.go | 7 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/struct.go | 83 |
9 files changed, 184 insertions, 120 deletions
diff --git a/vendor/gopkg.in/ini.v1/.travis.yml b/vendor/gopkg.in/ini.v1/.travis.yml index c8ea49ccc6..08682ef840 100644 --- a/vendor/gopkg.in/ini.v1/.travis.yml +++ b/vendor/gopkg.in/ini.v1/.travis.yml @@ -7,7 +7,9 @@ go: - 1.9.x - 1.10.x - 1.11.x + - 1.12.x +install: skip script: - go get golang.org/x/tools/cmd/cover - go get github.com/smartystreets/goconvey diff --git a/vendor/gopkg.in/ini.v1/README.md b/vendor/gopkg.in/ini.v1/README.md index ae4dfc3a5a..036c56d63b 100644 --- a/vendor/gopkg.in/ini.v1/README.md +++ b/vendor/gopkg.in/ini.v1/README.md @@ -22,19 +22,27 @@ Package ini provides INI file read and write functionality in Go. The minimum requirement of Go is **1.6**. -To use a tagged revision: - ```sh $ go get gopkg.in/ini.v1 ``` -To use with latest changes: +Please add `-u` flag to update in the future. + +## Go Modules + +For historical reason, people use two different import paths for this package: `github.com/go-ini/ini` and `gopkg.in/ini.v1`. If you get error similar to the following one: -```sh -$ go get github.com/go-ini/ini +``` +go: finding github.com/go-ini/ini v0.0.0-00010101000000-000000000000 +go: github.com/go-ini/ini@v0.0.0-00010101000000-000000000000: unknown revision 000000000000 +go: error loading module requirements ``` -Please add `-u` flag to update in the future. +It is because one of your dependencies is using deprecated import path `github.com/go-ini/ini`, you can make a quick fix by adding the following line to your `go.mod` file (`v.1.44.0` was the latest version tagged on `master` branch): + +``` +replace github.com/go-ini/ini => gopkg.in/ini.v1 v1.44.0 +``` ## Getting Help diff --git a/vendor/gopkg.in/ini.v1/error.go b/vendor/gopkg.in/ini.v1/error.go index 80afe74315..d88347c54b 100644 --- a/vendor/gopkg.in/ini.v1/error.go +++ b/vendor/gopkg.in/ini.v1/error.go @@ -18,10 +18,12 @@ import ( "fmt" ) +// ErrDelimiterNotFound indicates the error type of no delimiter is found which there should be one. type ErrDelimiterNotFound struct { Line string } +// IsErrDelimiterNotFound returns true if the given error is an instance of ErrDelimiterNotFound. func IsErrDelimiterNotFound(err error) bool { _, ok := err.(ErrDelimiterNotFound) return ok diff --git a/vendor/gopkg.in/ini.v1/file.go b/vendor/gopkg.in/ini.v1/file.go index 0ed0eafd02..b38aadd1f8 100644 --- a/vendor/gopkg.in/ini.v1/file.go +++ b/vendor/gopkg.in/ini.v1/file.go @@ -68,7 +68,7 @@ func Empty() *File { func (f *File) NewSection(name string) (*Section, error) { if len(name) == 0 { return nil, errors.New("error creating new section: empty section name") - } else if f.options.Insensitive && name != DEFAULT_SECTION { + } else if f.options.Insensitive && name != DefaultSection { name = strings.ToLower(name) } @@ -111,7 +111,7 @@ func (f *File) NewSections(names ...string) (err error) { // GetSection returns section by given name. func (f *File) GetSection(name string) (*Section, error) { if len(name) == 0 { - name = DEFAULT_SECTION + name = DefaultSection } if f.options.Insensitive { name = strings.ToLower(name) @@ -141,7 +141,7 @@ func (f *File) Section(name string) *Section { return sec } -// Section returns list of Section. +// Sections returns a list of Section stored in the current instance. func (f *File) Sections() []*Section { if f.BlockMode { f.lock.RLock() @@ -175,7 +175,7 @@ func (f *File) DeleteSection(name string) { } if len(name) == 0 { - name = DEFAULT_SECTION + name = DefaultSection } for i, s := range f.sectionList { @@ -306,7 +306,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { for _, kname := range sec.keyList { key := sec.Key(kname) if len(key.Comment) > 0 { - if len(indent) > 0 && sname != DEFAULT_SECTION { + if len(indent) > 0 && sname != DefaultSection { buf.WriteString(indent) } @@ -325,7 +325,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { } } - if len(indent) > 0 && sname != DEFAULT_SECTION { + if len(indent) > 0 && sname != DefaultSection { buf.WriteString(indent) } diff --git a/vendor/gopkg.in/ini.v1/ini.go b/vendor/gopkg.in/ini.v1/ini.go index f827a1ef99..36c072cdf3 100644 --- a/vendor/gopkg.in/ini.v1/ini.go +++ b/vendor/gopkg.in/ini.v1/ini.go @@ -28,44 +28,46 @@ import ( ) const ( - // Name for default section. You can use this constant or the string literal. + // DefaultSection is the name of default section. You can use this constant or the string literal. // In most of cases, an empty string is all you need to access the section. - DEFAULT_SECTION = "DEFAULT" + DefaultSection = "DEFAULT" + // Deprecated: Use "DefaultSection" instead. + DEFAULT_SECTION = DefaultSection // Maximum allowed depth when recursively substituing variable names. - _DEPTH_VALUES = 99 - _VERSION = "1.42.0" + depthValues = 99 + version = "1.46.0" ) // Version returns current package version literal. func Version() string { - return _VERSION + return version } var ( - // Delimiter to determine or compose a new line. - // This variable will be changed to "\r\n" automatically on Windows - // at package init time. + // LineBreak is the delimiter to determine or compose a new line. + // This variable will be changed to "\r\n" automatically on Windows at package init time. LineBreak = "\n" - // Place custom spaces when PrettyFormat and PrettyEqual are both disabled - DefaultFormatLeft = "" + // DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled. + DefaultFormatLeft = "" + // DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled. DefaultFormatRight = "" // Variable regexp pattern: %(variable)s varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`) - // Indicate whether to align "=" sign with spaces to produce pretty output + // PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output // or reduce all possible spaces for compact format. PrettyFormat = true - // Place spaces around "=" sign even when PrettyFormat is false + // PrettyEqual places spaces around "=" sign even when PrettyFormat is false. PrettyEqual = false - // Explicitly write DEFAULT section header + // DefaultHeader explicitly writes default section header. DefaultHeader = false - // Indicate whether to put a line between sections + // PrettySection indicates whether to put a line between sections. PrettySection = true ) @@ -129,6 +131,7 @@ func parseDataSource(source interface{}) (dataSource, error) { } } +// LoadOptions contains all customized options used for load data source(s). type LoadOptions struct { // Loose indicates whether the parser should ignore nonexistent files or return error. Loose bool @@ -174,6 +177,7 @@ type LoadOptions struct { PreserveSurroundedQuote bool } +// LoadSources allows caller to apply customized options for loading from data source(s). func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) { sources := make([]dataSource, len(others)+1) sources[0], err = parseDataSource(source) diff --git a/vendor/gopkg.in/ini.v1/key.go b/vendor/gopkg.in/ini.v1/key.go index 0fee0dc7e4..38860ff4bd 100644 --- a/vendor/gopkg.in/ini.v1/key.go +++ b/vendor/gopkg.in/ini.v1/key.go @@ -77,6 +77,7 @@ func (k *Key) addNestedValue(val string) error { return nil } +// AddNestedValue adds a nested value to the key. func (k *Key) AddNestedValue(val string) error { if !k.s.f.options.AllowNestedValues { return errors.New("nested value is not allowed") @@ -126,7 +127,7 @@ func (k *Key) transformValue(val string) string { if !strings.Contains(val, "%") { return val } - for i := 0; i < _DEPTH_VALUES; i++ { + for i := 0; i < depthValues; i++ { vr := varPattern.FindString(val) if len(vr) == 0 { break @@ -186,8 +187,8 @@ func (k *Key) Float64() (float64, error) { // Int returns int type value. func (k *Key) Int() (int, error) { - v, err := strconv.ParseInt(k.String(), 0, 64) - return int(v), err + v, err := strconv.ParseInt(k.String(), 0, 64) + return int(v), err } // Int64 returns int64 type value. @@ -491,7 +492,7 @@ func (k *Key) Strings(delim string) []string { buf.WriteRune(runes[idx]) } } - idx += 1 + idx++ if idx == len(runes) { break } @@ -669,7 +670,7 @@ func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, vals := make([]int, 0, len(strs)) for _, str := range strs { valInt64, err := strconv.ParseInt(str, 0, 64) - val := int(valInt64) + val := int(valInt64) if err != nil && returnOnInvalid { return nil, err } diff --git a/vendor/gopkg.in/ini.v1/parser.go b/vendor/gopkg.in/ini.v1/parser.go index f20073d1b4..7c22a25c1a 100644 --- a/vendor/gopkg.in/ini.v1/parser.go +++ b/vendor/gopkg.in/ini.v1/parser.go @@ -27,25 +27,29 @@ import ( var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)") -type tokenType int - -const ( - _TOKEN_INVALID tokenType = iota - _TOKEN_COMMENT - _TOKEN_SECTION - _TOKEN_KEY -) +type parserOptions struct { + IgnoreContinuation bool + IgnoreInlineComment bool + AllowPythonMultilineValues bool + SpaceBeforeInlineComment bool + UnescapeValueDoubleQuotes bool + UnescapeValueCommentSymbols bool + PreserveSurroundedQuote bool +} type parser struct { buf *bufio.Reader + options parserOptions + isEOF bool count int comment *bytes.Buffer } -func newParser(r io.Reader) *parser { +func newParser(r io.Reader, opts parserOptions) *parser { return &parser{ buf: bufio.NewReader(r), + options: opts, count: 1, comment: &bytes.Buffer{}, } @@ -196,12 +200,13 @@ func hasSurroundedQuote(in string, quote byte) bool { strings.IndexByte(in[1:], quote) == len(in)-2 } -func (p *parser) readValue(in []byte, - parserBufferSize int, - ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols, allowPythonMultilines, spaceBeforeInlineComment, preserveSurroundedQuote bool) (string, error) { +func (p *parser) readValue(in []byte, bufferSize int) (string, error) { line := strings.TrimLeftFunc(string(in), unicode.IsSpace) if len(line) == 0 { + if p.options.AllowPythonMultilineValues && len(in) > 0 && in[len(in)-1] == '\n' { + return p.readPythonMultilines(line, bufferSize) + } return "", nil } @@ -210,7 +215,7 @@ func (p *parser) readValue(in []byte, valQuote = `"""` } else if line[0] == '`' { valQuote = "`" - } else if unescapeValueDoubleQuotes && line[0] == '"' { + } else if p.options.UnescapeValueDoubleQuotes && line[0] == '"' { valQuote = `"` } @@ -222,7 +227,7 @@ func (p *parser) readValue(in []byte, return p.readMultilines(line, line[startIdx:], valQuote) } - if unescapeValueDoubleQuotes && valQuote == `"` { + if p.options.UnescapeValueDoubleQuotes && valQuote == `"` { return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil } return line[startIdx : pos+startIdx], nil @@ -234,14 +239,14 @@ func (p *parser) readValue(in []byte, trimmedLastChar := line[len(line)-1] // Check continuation lines when desired - if !ignoreContinuation && trimmedLastChar == '\\' { + if !p.options.IgnoreContinuation && trimmedLastChar == '\\' { return p.readContinuationLines(line[:len(line)-1]) } // Check if ignore inline comment - if !ignoreInlineComment { + if !p.options.IgnoreInlineComment { var i int - if spaceBeforeInlineComment { + if p.options.SpaceBeforeInlineComment { i = strings.Index(line, " #") if i == -1 { i = strings.Index(line, " ;") @@ -260,65 +265,75 @@ func (p *parser) readValue(in []byte, // Trim single and double quotes if (hasSurroundedQuote(line, '\'') || - hasSurroundedQuote(line, '"')) && !preserveSurroundedQuote { + hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote { line = line[1 : len(line)-1] - } else if len(valQuote) == 0 && unescapeValueCommentSymbols { + } else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols { if strings.Contains(line, `\;`) { line = strings.Replace(line, `\;`, ";", -1) } if strings.Contains(line, `\#`) { line = strings.Replace(line, `\#`, "#", -1) } - } else if allowPythonMultilines && lastChar == '\n' { - parserBufferPeekResult, _ := p.buf.Peek(parserBufferSize) - peekBuffer := bytes.NewBuffer(parserBufferPeekResult) + } else if p.options.AllowPythonMultilineValues && lastChar == '\n' { + return p.readPythonMultilines(line, bufferSize) + } - val := line + return line, nil +} - for { - peekData, peekErr := peekBuffer.ReadBytes('\n') - if peekErr != nil { - if peekErr == io.EOF { - return val, nil - } - return "", peekErr - } +func (p *parser) readPythonMultilines(line string, bufferSize int) (string, error) { + parserBufferPeekResult, _ := p.buf.Peek(bufferSize) + peekBuffer := bytes.NewBuffer(parserBufferPeekResult) - peekMatches := pythonMultiline.FindStringSubmatch(string(peekData)) - if len(peekMatches) != 3 { - return val, nil + for { + peekData, peekErr := peekBuffer.ReadBytes('\n') + if peekErr != nil { + if peekErr == io.EOF { + return line, nil } + return "", peekErr + } - // NOTE: Return if not a python-ini multi-line value. - currentIdentSize := len(peekMatches[1]) - if currentIdentSize <= 0 { - return val, nil - } + peekMatches := pythonMultiline.FindStringSubmatch(string(peekData)) + if len(peekMatches) != 3 { + return line, nil + } - // NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer. - _, err := p.readUntil('\n') - if err != nil { - return "", err - } + // NOTE: Return if not a python-ini multi-line value. + currentIdentSize := len(peekMatches[1]) + if currentIdentSize <= 0 { + return line, nil + } - val += fmt.Sprintf("\n%s", peekMatches[2]) + // NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer. + _, err := p.readUntil('\n') + if err != nil { + return "", err } - } - return line, nil + line += fmt.Sprintf("\n%s", peekMatches[2]) + } } // parse parses data through an io.Reader. func (f *File) parse(reader io.Reader) (err error) { - p := newParser(reader) + p := newParser(reader, parserOptions{ + IgnoreContinuation: f.options.IgnoreContinuation, + IgnoreInlineComment: f.options.IgnoreInlineComment, + AllowPythonMultilineValues: f.options.AllowPythonMultilineValues, + SpaceBeforeInlineComment: f.options.SpaceBeforeInlineComment, + UnescapeValueDoubleQuotes: f.options.UnescapeValueDoubleQuotes, + UnescapeValueCommentSymbols: f.options.UnescapeValueCommentSymbols, + PreserveSurroundedQuote: f.options.PreserveSurroundedQuote, + }) if err = p.BOM(); err != nil { return fmt.Errorf("BOM: %v", err) } // Ignore error because default section name is never empty string. - name := DEFAULT_SECTION + name := DefaultSection if f.options.Insensitive { - name = strings.ToLower(DEFAULT_SECTION) + name = strings.ToLower(DefaultSection) } section, _ := f.NewSection(name) @@ -426,15 +441,7 @@ func (f *File) parse(reader io.Reader) (err error) { if IsErrDelimiterNotFound(err) { switch { case f.options.AllowBooleanKeys: - kname, err := p.readValue(line, - parserBufferSize, - f.options.IgnoreContinuation, - f.options.IgnoreInlineComment, - f.options.UnescapeValueDoubleQuotes, - f.options.UnescapeValueCommentSymbols, - f.options.AllowPythonMultilineValues, - f.options.SpaceBeforeInlineComment, - f.options.PreserveSurroundedQuote) + kname, err := p.readValue(line, parserBufferSize) if err != nil { return err } @@ -461,15 +468,7 @@ func (f *File) parse(reader io.Reader) (err error) { p.count++ } - value, err := p.readValue(line[offset:], - parserBufferSize, - f.options.IgnoreContinuation, - f.options.IgnoreInlineComment, - f.options.UnescapeValueDoubleQuotes, - f.options.UnescapeValueCommentSymbols, - f.options.AllowPythonMultilineValues, - f.options.SpaceBeforeInlineComment, - f.options.PreserveSurroundedQuote) + value, err := p.readValue(line[offset:], parserBufferSize) if err != nil { return err } diff --git a/vendor/gopkg.in/ini.v1/section.go b/vendor/gopkg.in/ini.v1/section.go index bc32c620d6..0bd3e13015 100644 --- a/vendor/gopkg.in/ini.v1/section.go +++ b/vendor/gopkg.in/ini.v1/section.go @@ -106,7 +106,6 @@ func (s *Section) NewBooleanKey(name string) (*Key, error) { // GetKey returns key in section by given name. func (s *Section) GetKey(name string) (*Key, error) { - // FIXME: change to section level lock? if s.f.BlockMode { s.f.lock.RLock() } @@ -129,9 +128,8 @@ func (s *Section) GetKey(name string) (*Key, error) { continue } return sec.GetKey(name) - } else { - break } + break } return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name) } @@ -144,8 +142,7 @@ func (s *Section) HasKey(name string) bool { return key != nil } -// Haskey is a backwards-compatible name for HasKey. -// TODO: delete me in v2 +// Deprecated: Use "HasKey" instead. func (s *Section) Haskey(name string) bool { return s.HasKey(name) } diff --git a/vendor/gopkg.in/ini.v1/struct.go b/vendor/gopkg.in/ini.v1/struct.go index a9dfed078a..c713f8296c 100644 --- a/vendor/gopkg.in/ini.v1/struct.go +++ b/vendor/gopkg.in/ini.v1/struct.go @@ -149,7 +149,7 @@ func wrapStrictError(err error, isStrict bool) error { // 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. +// because we want to use default value that is already assigned to struct. func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { switch t.Kind() { case reflect.String: @@ -205,6 +205,17 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri field.Set(reflect.ValueOf(timeVal)) case reflect.Slice: return setSliceWithProperType(key, field, delim, allowShadow, isStrict) + case reflect.Ptr: + switch t.Elem().Kind() { + case reflect.Bool: + boolVal, err := key.Bool() + if err != nil { + return wrapStrictError(err, isStrict) + } + field.Set(reflect.ValueOf(&boolVal)) + default: + return fmt.Errorf("unsupported type '%s'", t) + } default: return fmt.Errorf("unsupported type '%s'", t) } @@ -244,14 +255,21 @@ func (s *Section) mapTo(val reflect.Value, isStrict bool) error { continue } - isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous isStruct := tpField.Type.Kind() == reflect.Struct + isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct + isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous if isAnonymous { field.Set(reflect.New(tpField.Type.Elem())) } - if isAnonymous || isStruct { + if isAnonymous || isStruct || isStructPtr { if sec, err := s.f.GetSection(fieldName); err == nil { + // Only set the field to non-nil struct value if we have + // a section for it. Otherwise, we end up with a non-nil + // struct ptr even though there is no data. + if isStructPtr && field.IsNil() { + field.Set(reflect.New(tpField.Type.Elem())) + } if err = sec.mapTo(field, isStrict); err != nil { return fmt.Errorf("error mapping field(%s): %v", fieldName, err) } @@ -283,7 +301,7 @@ func (s *Section) MapTo(v interface{}) error { return s.mapTo(val, false) } -// MapTo maps section to given struct in strict mode, +// StrictMapTo 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) @@ -303,13 +321,13 @@ func (f *File) MapTo(v interface{}) error { return f.Section("").MapTo(v) } -// MapTo maps file to given struct in strict mode, +// StrictMapTo 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. +// MapToWithMapper 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 { @@ -342,14 +360,43 @@ func StrictMapTo(v, source interface{}, others ...interface{}) error { } // reflectSliceWithProperType does the opposite thing as setSliceWithProperType. -func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error { +func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error { slice := field.Slice(0, field.Len()) if field.Len() == 0 { return nil } + sliceOf := field.Type().Elem().Kind() + + if allowShadow { + var keyWithShadows *Key + for i := 0; i < field.Len(); i++ { + var val string + switch sliceOf { + case reflect.String: + val = slice.Index(i).String() + case reflect.Int, reflect.Int64: + val = fmt.Sprint(slice.Index(i).Int()) + case reflect.Uint, reflect.Uint64: + val = fmt.Sprint(slice.Index(i).Uint()) + case reflect.Float64: + val = fmt.Sprint(slice.Index(i).Float()) + case reflectTime: + val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339) + default: + return fmt.Errorf("unsupported type '[]%s'", sliceOf) + } + + if i == 0 { + keyWithShadows = newKey(key.s, key.name, val) + } else { + keyWithShadows.AddShadow(val) + } + } + key = keyWithShadows + return nil + } var buf bytes.Buffer - sliceOf := field.Type().Elem().Kind() for i := 0; i < field.Len(); i++ { switch sliceOf { case reflect.String: @@ -367,12 +414,12 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) err } buf.WriteString(delim) } - key.SetValue(buf.String()[:buf.Len()-1]) + key.SetValue(buf.String()[:buf.Len()-len(delim)]) return nil } // reflectWithProperType does the opposite thing as setWithProperType. -func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error { +func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error { switch t.Kind() { case reflect.String: key.SetValue(field.String()) @@ -387,7 +434,11 @@ func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim case reflectTime: key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339))) case reflect.Slice: - return reflectSliceWithProperType(key, field, delim) + return reflectSliceWithProperType(key, field, delim, allowShadow) + case reflect.Ptr: + if !field.IsNil() { + return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow) + } default: return fmt.Errorf("unsupported type '%s'", t) } @@ -432,12 +483,12 @@ func (s *Section) reflectFrom(val reflect.Value) error { continue } - opts := strings.SplitN(tag, ",", 2) - if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) { + rawName, omitEmpty, allowShadow := parseTagOptions(tag) + if omitEmpty && isEmptyValue(field) { continue } - fieldName := s.parseFieldName(tpField.Name, opts[0]) + fieldName := s.parseFieldName(tpField.Name, rawName) if len(fieldName) == 0 || !field.CanSet() { continue } @@ -473,7 +524,7 @@ func (s *Section) reflectFrom(val reflect.Value) error { key.Comment = tpField.Tag.Get("comment") } - if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil { + if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim")), allowShadow); err != nil { return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) } @@ -500,7 +551,7 @@ func (f *File) ReflectFrom(v interface{}) error { return f.Section("").ReflectFrom(v) } -// ReflectFrom reflects data sources from given struct with name mapper. +// ReflectFromWithMapper 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) |