From 7b320119ba532fd409ec7dade7ad02011c309599 Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Wed, 18 Oct 2017 13:15:14 +0100 Subject: Update dependencies --- vendor/github.com/go-ini/ini/LICENSE | 2 +- vendor/github.com/go-ini/ini/README.md | 37 +++++++++-- vendor/github.com/go-ini/ini/README_ZH.md | 37 +++++++++-- vendor/github.com/go-ini/ini/ini.go | 78 +++++++++++----------- vendor/github.com/go-ini/ini/key.go | 37 +++++++++-- vendor/github.com/go-ini/ini/parser.go | 21 ++++-- vendor/github.com/go-ini/ini/struct.go | 104 ++++++++++++++++++++++++------ 7 files changed, 235 insertions(+), 81 deletions(-) (limited to 'vendor/github.com/go-ini/ini') diff --git a/vendor/github.com/go-ini/ini/LICENSE b/vendor/github.com/go-ini/ini/LICENSE index 37ec93a..d361bbc 100644 --- a/vendor/github.com/go-ini/ini/LICENSE +++ b/vendor/github.com/go-ini/ini/LICENSE @@ -176,7 +176,7 @@ recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + 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. diff --git a/vendor/github.com/go-ini/ini/README.md b/vendor/github.com/go-ini/ini/README.md index 8594742..f4ff27c 100644 --- a/vendor/github.com/go-ini/ini/README.md +++ b/vendor/github.com/go-ini/ini/README.md @@ -83,8 +83,8 @@ sec1, err := cfg.GetSection("Section") sec2, err := cfg.GetSection("SecTIOn") // key1 and key2 are the exactly same key object -key1, err := cfg.GetKey("Key") -key2, err := cfg.GetKey("KeY") +key1, err := sec1.GetKey("Key") +key2, err := sec2.GetKey("KeY") ``` #### MySQL-like boolean key @@ -101,7 +101,7 @@ skip-name-resolve By default, this is considered as missing value. But if you know you're going to deal with those cases, you can assign advanced load options: ```go -cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf")) +cfg, err := ini.LoadSources(ini.LoadOptions{AllowBooleanKeys: true}, "my.cnf")) ``` The value of those keys are always `true`, and when you save to a file, it will keep in the same foramt as you read. @@ -122,6 +122,12 @@ Take care that following format will be treated as comment: If you want to save a value with `#` or `;`, please quote them with ``` ` ``` or ``` """ ```. +Alternatively, you can use following `LoadOptions` to completely ignore inline comments: + +```go +cfg, err := ini.LoadSources(ini.LoadOptions{IgnoreInlineComment: true}, "app.ini")) +``` + ### Working with sections To get a section, you would need to: @@ -323,6 +329,20 @@ foo = "some value" // foo: some value bar = 'some value' // bar: some value ``` +Sometimes you downloaded file from [Crowdin](https://crowdin.com/) has values like the following (value is surrounded by double quotes and quotes in the value are escaped): + +```ini +create_repo="created repository %s" +``` + +How do you transform this to regular format automatically? + +```go +cfg, err := ini.LoadSources(ini.LoadOptions{UnescapeValueDoubleQuotes: true}, "en-US.ini")) +cfg.Section("").Key("create_repo").String() +// You got: created repository %s +``` + That's all? Hmm, no. #### Helper methods of working with values @@ -474,7 +494,7 @@ cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"] Sometimes, you have sections that do not contain key-value pairs but raw content, to handle such case, you can use `LoadOptions.UnparsableSections`: ```go -cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] +cfg, err := ini.LoadSources(ini.LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] <1> This slide has the fuel listed in the wrong units `)) body := cfg.Section("COMMENTS").Body() @@ -567,7 +587,7 @@ Why not? ```go type Embeded struct { - Dates []time.Time `delim:"|"` + Dates []time.Time `delim:"|" comment:"Time data"` Places []string `ini:"places,omitempty"` None []int `ini:",omitempty"` } @@ -575,10 +595,10 @@ type Embeded struct { type Author struct { Name string `ini:"NAME"` Male bool - Age int + Age int `comment:"Author's age"` GPA float64 NeverMind string `ini:"-"` - *Embeded + *Embeded `comment:"Embeded section"` } func main() { @@ -599,10 +619,13 @@ So, what do I get? ```ini NAME = Unknwon Male = true +; Author's age Age = 21 GPA = 2.8 +; Embeded section [Embeded] +; Time data Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00 places = HangZhou,Boston ``` diff --git a/vendor/github.com/go-ini/ini/README_ZH.md b/vendor/github.com/go-ini/ini/README_ZH.md index 163432d..69aefef 100644 --- a/vendor/github.com/go-ini/ini/README_ZH.md +++ b/vendor/github.com/go-ini/ini/README_ZH.md @@ -76,8 +76,8 @@ sec1, err := cfg.GetSection("Section") sec2, err := cfg.GetSection("SecTIOn") // key1 和 key2 指向同一个键对象 -key1, err := cfg.GetKey("Key") -key2, err := cfg.GetKey("KeY") +key1, err := sec1.GetKey("Key") +key2, err := sec2.GetKey("KeY") ``` #### 类似 MySQL 配置中的布尔值键 @@ -94,7 +94,7 @@ skip-name-resolve 默认情况下这被认为是缺失值而无法完成解析,但可以通过高级的加载选项对它们进行处理: ```go -cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf")) +cfg, err := ini.LoadSources(ini.LoadOptions{AllowBooleanKeys: true}, "my.cnf")) ``` 这些键的值永远为 `true`,且在保存到文件时也只会输出键名。 @@ -115,6 +115,12 @@ key, err := sec.NewBooleanKey("skip-host-cache") 如果你希望使用包含 `#` 或 `;` 的值,请使用 ``` ` ``` 或 ``` """ ``` 进行包覆。 +除此之外,您还可以通过 `LoadOptions` 完全忽略行内注释: + +```go +cfg, err := ini.LoadSources(ini.LoadOptions{IgnoreInlineComment: true}, "app.ini")) +``` + ### 操作分区(Section) 获取指定分区: @@ -316,6 +322,20 @@ foo = "some value" // foo: some value bar = 'some value' // bar: some value ``` +有时您会获得像从 [Crowdin](https://crowdin.com/) 网站下载的文件那样具有特殊格式的值(值使用双引号括起来,内部的双引号被转义): + +```ini +create_repo="创建了仓库 %s" +``` + +那么,怎么自动地将这类值进行处理呢? + +```go +cfg, err := ini.LoadSources(ini.LoadOptions{UnescapeValueDoubleQuotes: true}, "en-US.ini")) +cfg.Section("").Key("create_repo").String() +// You got: 创建了仓库 %s +``` + 这就是全部了?哈哈,当然不是。 #### 操作键值的辅助方法 @@ -467,7 +487,7 @@ cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"] 如果遇到一些比较特殊的分区,它们不包含常见的键值对,而是没有固定格式的纯文本,则可以使用 `LoadOptions.UnparsableSections` 进行处理: ```go -cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] +cfg, err := LoadSources(ini.LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] <1> This slide has the fuel listed in the wrong units `)) body := cfg.Section("COMMENTS").Body() @@ -558,7 +578,7 @@ p := &Person{ ```go type Embeded struct { - Dates []time.Time `delim:"|"` + Dates []time.Time `delim:"|" comment:"Time data"` Places []string `ini:"places,omitempty"` None []int `ini:",omitempty"` } @@ -566,10 +586,10 @@ type Embeded struct { type Author struct { Name string `ini:"NAME"` Male bool - Age int + Age int `comment:"Author's age"` GPA float64 NeverMind string `ini:"-"` - *Embeded + *Embeded `comment:"Embeded section"` } func main() { @@ -590,10 +610,13 @@ func main() { ```ini NAME = Unknwon Male = true +; Author's age Age = 21 GPA = 2.8 +; Embeded section [Embeded] +; Time data Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00 places = HangZhou,Boston ``` diff --git a/vendor/github.com/go-ini/ini/ini.go b/vendor/github.com/go-ini/ini/ini.go index 5211d5a..07dc62c 100644 --- a/vendor/github.com/go-ini/ini/ini.go +++ b/vendor/github.com/go-ini/ini/ini.go @@ -24,10 +24,8 @@ import ( "os" "regexp" "runtime" - "strconv" "strings" "sync" - "time" ) const ( @@ -37,7 +35,7 @@ const ( // Maximum allowed depth when recursively substituing variable names. _DEPTH_VALUES = 99 - _VERSION = "1.27.0" + _VERSION = "1.30.0" ) // Version returns current package version literal. @@ -60,6 +58,9 @@ var ( // Explicitly write DEFAULT section header DefaultHeader = false + + // Indicate whether to put a line between sections + PrettySection = true ) func init() { @@ -180,6 +181,8 @@ type LoadOptions struct { AllowBooleanKeys bool // AllowShadows indicates whether to keep track of keys with same name under same section. AllowShadows bool + // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" + UnescapeValueDoubleQuotes bool // Some INI formats allow group blocks that store a block of raw content that doesn't otherwise // conform to key/value pairs. Specify the names of those blocks here. UnparseableSections []string @@ -395,10 +398,7 @@ func (f *File) Append(source interface{}, others ...interface{}) error { return f.Reload() } -// WriteToIndent writes content into io.Writer with given indention. -// If PrettyFormat has been set to be true, -// it will align "=" sign with spaces under each section. -func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { +func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { equalSign := "=" if PrettyFormat { equalSign = " = " @@ -411,15 +411,17 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { if len(sec.Comment) > 0 { if sec.Comment[0] != '#' && sec.Comment[0] != ';' { sec.Comment = "; " + sec.Comment + } else { + sec.Comment = sec.Comment[:1] + " " + strings.TrimSpace(sec.Comment[1:]) } - if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil { - return 0, err + if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil { + return nil, err } } if i > 0 || DefaultHeader { - if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil { - return 0, err + if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { + return nil, err } } else { // Write nothing if default section is empty @@ -429,8 +431,8 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { } if sec.isRawSection { - if _, err = buf.WriteString(sec.rawBody); err != nil { - return 0, err + if _, err := buf.WriteString(sec.rawBody); err != nil { + return nil, err } continue } @@ -465,9 +467,11 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { } if key.Comment[0] != '#' && key.Comment[0] != ';' { key.Comment = "; " + key.Comment + } else { + key.Comment = key.Comment[:1] + " " + strings.TrimSpace(key.Comment[1:]) } - if _, err = buf.WriteString(key.Comment + LineBreak); err != nil { - return 0, err + if _, err := buf.WriteString(key.Comment + LineBreak); err != nil { + return nil, err } } @@ -485,8 +489,8 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { } for _, val := range key.ValueWithShadows() { - if _, err = buf.WriteString(kname); err != nil { - return 0, err + if _, err := buf.WriteString(kname); err != nil { + return nil, err } if key.isBooleanType { @@ -504,21 +508,34 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { // In case key value contains "\n", "`", "\"", "#" or ";" if strings.ContainsAny(val, "\n`") { val = `"""` + val + `"""` - } else if strings.ContainsAny(val, "#;") { + } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { val = "`" + val + "`" } - if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil { - return 0, err + if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { + return nil, err } } } - // Put a line between sections - if _, err = buf.WriteString(LineBreak); err != nil { - return 0, err + if PrettySection { + // Put a line between sections + if _, err := buf.WriteString(LineBreak); err != nil { + return nil, err + } } } + return buf, nil +} + +// WriteToIndent writes content into io.Writer with given indention. +// If PrettyFormat has been set to be true, +// it will align "=" sign with spaces under each section. +func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) { + buf, err := f.writeToBuffer(indent) + if err != nil { + return 0, err + } return buf.WriteTo(w) } @@ -531,23 +548,12 @@ func (f *File) WriteTo(w io.Writer) (int64, error) { func (f *File) SaveToIndent(filename, indent string) error { // Note: Because we are truncating with os.Create, // so it's safer to save to a temporary file location and rename afte done. - tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp" - defer os.Remove(tmpPath) - - fw, err := os.Create(tmpPath) + buf, err := f.writeToBuffer(indent) if err != nil { return err } - if _, err = f.WriteToIndent(fw, indent); err != nil { - fw.Close() - return err - } - fw.Close() - - // Remove old file and rename the new one. - os.Remove(filename) - return os.Rename(tmpPath, filename) + return ioutil.WriteFile(filename, buf.Bytes(), 0666) } // SaveTo writes content to file system. diff --git a/vendor/github.com/go-ini/ini/key.go b/vendor/github.com/go-ini/ini/key.go index 838356a..0a2aa30 100644 --- a/vendor/github.com/go-ini/ini/key.go +++ b/vendor/github.com/go-ini/ini/key.go @@ -15,6 +15,7 @@ package ini import ( + "bytes" "errors" "fmt" "strconv" @@ -444,11 +445,39 @@ func (k *Key) Strings(delim string) []string { return []string{} } - vals := strings.Split(str, delim) - for i := range vals { - // vals[i] = k.transformValue(strings.TrimSpace(vals[i])) - vals[i] = strings.TrimSpace(vals[i]) + runes := []rune(str) + vals := make([]string, 0, 2) + var buf bytes.Buffer + escape := false + idx := 0 + for { + if escape { + escape = false + if runes[idx] != '\\' && !strings.HasPrefix(string(runes[idx:]), delim) { + buf.WriteRune('\\') + } + buf.WriteRune(runes[idx]) + } else { + if runes[idx] == '\\' { + escape = true + } else if strings.HasPrefix(string(runes[idx:]), delim) { + idx += len(delim) - 1 + vals = append(vals, strings.TrimSpace(buf.String())) + buf.Reset() + } else { + buf.WriteRune(runes[idx]) + } + } + idx += 1 + if idx == len(runes) { + break + } } + + if buf.Len() > 0 { + vals = append(vals, strings.TrimSpace(buf.String())) + } + return vals } diff --git a/vendor/github.com/go-ini/ini/parser.go b/vendor/github.com/go-ini/ini/parser.go index 6c0b107..861e366 100644 --- a/vendor/github.com/go-ini/ini/parser.go +++ b/vendor/github.com/go-ini/ini/parser.go @@ -189,11 +189,11 @@ func (p *parser) readContinuationLines(val string) (string, error) { // are quotes \" or \'. // It returns false if any other parts also contain same kind of quotes. func hasSurroundedQuote(in string, quote byte) bool { - return len(in) > 2 && in[0] == quote && in[len(in)-1] == quote && + return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote && strings.IndexByte(in[1:], quote) == len(in)-2 } -func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bool) (string, error) { +func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes bool) (string, error) { line := strings.TrimLeftFunc(string(in), unicode.IsSpace) if len(line) == 0 { return "", nil @@ -204,6 +204,8 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo valQuote = `"""` } else if line[0] == '`' { valQuote = "`" + } else if unescapeValueDoubleQuotes && line[0] == '"' { + valQuote = `"` } if len(valQuote) > 0 { @@ -214,6 +216,9 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo return p.readMultilines(line, line[startIdx:], valQuote) } + if unescapeValueDoubleQuotes && valQuote == `"` { + return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil + } return line[startIdx : pos+startIdx], nil } @@ -234,7 +239,7 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo } } - // Trim single quotes + // Trim single and double quotes if hasSurroundedQuote(line, '\'') || hasSurroundedQuote(line, '"') { line = line[1 : len(line)-1] @@ -321,7 +326,10 @@ func (f *File) parse(reader io.Reader) (err error) { if err != nil { // Treat as boolean key when desired, and whole line is key name. if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys { - kname, err := p.readValue(line, f.options.IgnoreContinuation, f.options.IgnoreInlineComment) + kname, err := p.readValue(line, + f.options.IgnoreContinuation, + f.options.IgnoreInlineComment, + f.options.UnescapeValueDoubleQuotes) if err != nil { return err } @@ -344,7 +352,10 @@ func (f *File) parse(reader io.Reader) (err error) { p.count++ } - value, err := p.readValue(line[offset:], f.options.IgnoreContinuation, f.options.IgnoreInlineComment) + value, err := p.readValue(line[offset:], + f.options.IgnoreContinuation, + f.options.IgnoreInlineComment, + f.options.UnescapeValueDoubleQuotes) if err != nil { return err } diff --git a/vendor/github.com/go-ini/ini/struct.go b/vendor/github.com/go-ini/ini/struct.go index 031c78b..e0bff67 100644 --- a/vendor/github.com/go-ini/ini/struct.go +++ b/vendor/github.com/go-ini/ini/struct.go @@ -78,7 +78,7 @@ func parseDelim(actual string) string { 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 bool) error { +func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { var strs []string if allowShadow { strs = key.StringsWithShadows(delim) @@ -92,26 +92,30 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh } var vals interface{} + var err error sliceOf := field.Type().Elem().Kind() switch sliceOf { case reflect.String: vals = strs case reflect.Int: - vals, _ = key.parseInts(strs, true, false) + vals, err = key.parseInts(strs, true, false) case reflect.Int64: - vals, _ = key.parseInt64s(strs, true, false) + vals, err = key.parseInt64s(strs, true, false) case reflect.Uint: - vals, _ = key.parseUints(strs, true, false) + vals, err = key.parseUints(strs, true, false) case reflect.Uint64: - vals, _ = key.parseUint64s(strs, true, false) + vals, err = key.parseUint64s(strs, true, false) case reflect.Float64: - vals, _ = key.parseFloat64s(strs, true, false) + vals, err = key.parseFloat64s(strs, true, false) case reflectTime: - vals, _ = key.parseTimesFormat(time.RFC3339, strs, true, false) + 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++ { @@ -136,10 +140,17 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh 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 bool) error { +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 { @@ -149,7 +160,7 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri case reflect.Bool: boolVal, err := key.Bool() if err != nil { - return nil + return wrapStrictError(err, isStrict) } field.SetBool(boolVal) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: @@ -161,8 +172,8 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri } intVal, err := key.Int64() - if err != nil || intVal == 0 { - return nil + if err != nil { + return wrapStrictError(err, isStrict) } field.SetInt(intVal) // byte is an alias for uint8, so supporting uint8 breaks support for byte @@ -176,24 +187,24 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri uintVal, err := key.Uint64() if err != nil { - return nil + return wrapStrictError(err, isStrict) } field.SetUint(uintVal) case reflect.Float32, reflect.Float64: floatVal, err := key.Float64() if err != nil { - return nil + return wrapStrictError(err, isStrict) } field.SetFloat(floatVal) case reflectTime: timeVal, err := key.Time() if err != nil { - return nil + return wrapStrictError(err, isStrict) } field.Set(reflect.ValueOf(timeVal)) case reflect.Slice: - return setSliceWithProperType(key, field, delim, allowShadow) + return setSliceWithProperType(key, field, delim, allowShadow, isStrict) default: return fmt.Errorf("unsupported type '%s'", t) } @@ -212,7 +223,7 @@ func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bo return rawName, omitEmpty, allowShadow } -func (s *Section) mapTo(val reflect.Value) error { +func (s *Section) mapTo(val reflect.Value, isStrict bool) error { if val.Kind() == reflect.Ptr { val = val.Elem() } @@ -241,7 +252,7 @@ func (s *Section) mapTo(val reflect.Value) error { if isAnonymous || isStruct { if sec, err := s.f.GetSection(fieldName); err == nil { - if err = sec.mapTo(field); err != nil { + if err = sec.mapTo(field, isStrict); err != nil { return fmt.Errorf("error mapping field(%s): %v", fieldName, err) } continue @@ -250,7 +261,7 @@ func (s *Section) mapTo(val reflect.Value) error { if key, err := s.GetKey(fieldName); err == nil { delim := parseDelim(tpField.Tag.Get("delim")) - if err = setWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil { + if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil { return fmt.Errorf("error mapping field(%s): %v", fieldName, err) } } @@ -269,7 +280,22 @@ func (s *Section) MapTo(v interface{}) error { return errors.New("cannot map to non-pointer struct") } - return s.mapTo(val) + 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. @@ -277,6 +303,12 @@ 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...) @@ -287,11 +319,28 @@ func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, other 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()) @@ -359,10 +408,11 @@ func isEmptyValue(v reflect.Value) bool { return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 - case reflectTime: - return v.Interface().(time.Time).IsZero() case reflect.Interface, reflect.Ptr: return v.IsNil() + case reflectTime: + t, ok := v.Interface().(time.Time) + return ok && t.IsZero() } return false } @@ -400,6 +450,12 @@ func (s *Section) reflectFrom(val reflect.Value) error { // 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) } @@ -411,6 +467,12 @@ func (s *Section) reflectFrom(val reflect.Value) error { 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) } -- cgit v1.2.3