aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/gopkg.in/ini.v1
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gopkg.in/ini.v1')
-rw-r--r--vendor/gopkg.in/ini.v1/.editorconfig12
-rw-r--r--vendor/gopkg.in/ini.v1/.gitignore7
-rw-r--r--vendor/gopkg.in/ini.v1/.golangci.yml27
-rw-r--r--vendor/gopkg.in/ini.v1/LICENSE191
-rw-r--r--vendor/gopkg.in/ini.v1/Makefile15
-rw-r--r--vendor/gopkg.in/ini.v1/README.md43
-rw-r--r--vendor/gopkg.in/ini.v1/codecov.yml16
-rw-r--r--vendor/gopkg.in/ini.v1/data_source.go76
-rw-r--r--vendor/gopkg.in/ini.v1/deprecated.go22
-rw-r--r--vendor/gopkg.in/ini.v1/error.go49
-rw-r--r--vendor/gopkg.in/ini.v1/file.go541
-rw-r--r--vendor/gopkg.in/ini.v1/helper.go24
-rw-r--r--vendor/gopkg.in/ini.v1/ini.go176
-rw-r--r--vendor/gopkg.in/ini.v1/key.go837
-rw-r--r--vendor/gopkg.in/ini.v1/parser.go520
-rw-r--r--vendor/gopkg.in/ini.v1/section.go256
-rw-r--r--vendor/gopkg.in/ini.v1/struct.go747
17 files changed, 0 insertions, 3559 deletions
diff --git a/vendor/gopkg.in/ini.v1/.editorconfig b/vendor/gopkg.in/ini.v1/.editorconfig
deleted file mode 100644
index 4a2d918..0000000
--- a/vendor/gopkg.in/ini.v1/.editorconfig
+++ /dev/null
@@ -1,12 +0,0 @@
1# http://editorconfig.org
2
3root = true
4
5[*]
6charset = utf-8
7end_of_line = lf
8insert_final_newline = true
9trim_trailing_whitespace = true
10
11[*_test.go]
12trim_trailing_whitespace = false
diff --git a/vendor/gopkg.in/ini.v1/.gitignore b/vendor/gopkg.in/ini.v1/.gitignore
deleted file mode 100644
index 588388b..0000000
--- a/vendor/gopkg.in/ini.v1/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
1testdata/conf_out.ini
2ini.sublime-project
3ini.sublime-workspace
4testdata/conf_reflect.ini
5.idea
6/.vscode
7.DS_Store
diff --git a/vendor/gopkg.in/ini.v1/.golangci.yml b/vendor/gopkg.in/ini.v1/.golangci.yml
deleted file mode 100644
index 631e369..0000000
--- a/vendor/gopkg.in/ini.v1/.golangci.yml
+++ /dev/null
@@ -1,27 +0,0 @@
1linters-settings:
2 staticcheck:
3 checks: [
4 "all",
5 "-SA1019" # There are valid use cases of strings.Title
6 ]
7 nakedret:
8 max-func-lines: 0 # Disallow any unnamed return statement
9
10linters:
11 enable:
12 - deadcode
13 - errcheck
14 - gosimple
15 - govet
16 - ineffassign
17 - staticcheck
18 - structcheck
19 - typecheck
20 - unused
21 - varcheck
22 - nakedret
23 - gofmt
24 - rowserrcheck
25 - unconvert
26 - goimports
27 - unparam
diff --git a/vendor/gopkg.in/ini.v1/LICENSE b/vendor/gopkg.in/ini.v1/LICENSE
deleted file mode 100644
index d361bbc..0000000
--- a/vendor/gopkg.in/ini.v1/LICENSE
+++ /dev/null
@@ -1,191 +0,0 @@
1Apache License
2Version 2.0, January 2004
3http://www.apache.org/licenses/
4
5TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
71. Definitions.
8
9"License" shall mean the terms and conditions for use, reproduction, and
10distribution as defined by Sections 1 through 9 of this document.
11
12"Licensor" shall mean the copyright owner or entity authorized by the copyright
13owner that is granting the License.
14
15"Legal Entity" shall mean the union of the acting entity and all other entities
16that control, are controlled by, or are under common control with that entity.
17For the purposes of this definition, "control" means (i) the power, direct or
18indirect, to cause the direction or management of such entity, whether by
19contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20outstanding shares, or (iii) beneficial ownership of such entity.
21
22"You" (or "Your") shall mean an individual or Legal Entity exercising
23permissions granted by this License.
24
25"Source" form shall mean the preferred form for making modifications, including
26but not limited to software source code, documentation source, and configuration
27files.
28
29"Object" form shall mean any form resulting from mechanical transformation or
30translation of a Source form, including but not limited to compiled object code,
31generated documentation, and conversions to other media types.
32
33"Work" shall mean the work of authorship, whether in Source or Object form, made
34available under the License, as indicated by a copyright notice that is included
35in or attached to the work (an example is provided in the Appendix below).
36
37"Derivative Works" shall mean any work, whether in Source or Object form, that
38is based on (or derived from) the Work and for which the editorial revisions,
39annotations, elaborations, or other modifications represent, as a whole, an
40original work of authorship. For the purposes of this License, Derivative Works
41shall not include works that remain separable from, or merely link (or bind by
42name) to the interfaces of, the Work and Derivative Works thereof.
43
44"Contribution" shall mean any work of authorship, including the original version
45of the Work and any modifications or additions to that Work or Derivative Works
46thereof, that is intentionally submitted to Licensor for inclusion in the Work
47by the copyright owner or by an individual or Legal Entity authorized to submit
48on behalf of the copyright owner. For the purposes of this definition,
49"submitted" means any form of electronic, verbal, or written communication sent
50to the Licensor or its representatives, including but not limited to
51communication on electronic mailing lists, source code control systems, and
52issue tracking systems that are managed by, or on behalf of, the Licensor for
53the purpose of discussing and improving the Work, but excluding communication
54that is conspicuously marked or otherwise designated in writing by the copyright
55owner as "Not a Contribution."
56
57"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58of whom a Contribution has been received by Licensor and subsequently
59incorporated within the Work.
60
612. Grant of Copyright License.
62
63Subject to the terms and conditions of this License, each Contributor hereby
64grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65irrevocable copyright license to reproduce, prepare Derivative Works of,
66publicly display, publicly perform, sublicense, and distribute the Work and such
67Derivative Works in Source or Object form.
68
693. Grant of Patent License.
70
71Subject to the terms and conditions of this License, each Contributor hereby
72grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73irrevocable (except as stated in this section) patent license to make, have
74made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75such license applies only to those patent claims licensable by such Contributor
76that are necessarily infringed by their Contribution(s) alone or by combination
77of their Contribution(s) with the Work to which such Contribution(s) was
78submitted. If You institute patent litigation against any entity (including a
79cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80Contribution incorporated within the Work constitutes direct or contributory
81patent infringement, then any patent licenses granted to You under this License
82for that Work shall terminate as of the date such litigation is filed.
83
844. Redistribution.
85
86You may reproduce and distribute copies of the Work or Derivative Works thereof
87in any medium, with or without modifications, and in Source or Object form,
88provided that You meet the following conditions:
89
90You must give any other recipients of the Work or Derivative Works a copy of
91this License; and
92You must cause any modified files to carry prominent notices stating that You
93changed the files; and
94You must retain, in the Source form of any Derivative Works that You distribute,
95all copyright, patent, trademark, and attribution notices from the Source form
96of the Work, excluding those notices that do not pertain to any part of the
97Derivative Works; and
98If the Work includes a "NOTICE" text file as part of its distribution, then any
99Derivative Works that You distribute must include a readable copy of the
100attribution notices contained within such NOTICE file, excluding those notices
101that do not pertain to any part of the Derivative Works, in at least one of the
102following places: within a NOTICE text file distributed as part of the
103Derivative Works; within the Source form or documentation, if provided along
104with the Derivative Works; or, within a display generated by the Derivative
105Works, if and wherever such third-party notices normally appear. The contents of
106the NOTICE file are for informational purposes only and do not modify the
107License. You may add Your own attribution notices within Derivative Works that
108You distribute, alongside or as an addendum to the NOTICE text from the Work,
109provided that such additional attribution notices cannot be construed as
110modifying the License.
111You may add Your own copyright statement to Your modifications and may provide
112additional or different license terms and conditions for use, reproduction, or
113distribution of Your modifications, or for any such Derivative Works as a whole,
114provided Your use, reproduction, and distribution of the Work otherwise complies
115with the conditions stated in this License.
116
1175. Submission of Contributions.
118
119Unless You explicitly state otherwise, any Contribution intentionally submitted
120for inclusion in the Work by You to the Licensor shall be under the terms and
121conditions of this License, without any additional terms or conditions.
122Notwithstanding the above, nothing herein shall supersede or modify the terms of
123any separate license agreement you may have executed with Licensor regarding
124such Contributions.
125
1266. Trademarks.
127
128This License does not grant permission to use the trade names, trademarks,
129service marks, or product names of the Licensor, except as required for
130reasonable and customary use in describing the origin of the Work and
131reproducing the content of the NOTICE file.
132
1337. Disclaimer of Warranty.
134
135Unless required by applicable law or agreed to in writing, Licensor provides the
136Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138including, without limitation, any warranties or conditions of TITLE,
139NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140solely responsible for determining the appropriateness of using or
141redistributing the Work and assume any risks associated with Your exercise of
142permissions under this License.
143
1448. Limitation of Liability.
145
146In no event and under no legal theory, whether in tort (including negligence),
147contract, or otherwise, unless required by applicable law (such as deliberate
148and grossly negligent acts) or agreed to in writing, shall any Contributor be
149liable to You for damages, including any direct, indirect, special, incidental,
150or consequential damages of any character arising as a result of this License or
151out of the use or inability to use the Work (including but not limited to
152damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153any and all other commercial damages or losses), even if such Contributor has
154been advised of the possibility of such damages.
155
1569. Accepting Warranty or Additional Liability.
157
158While redistributing the Work or Derivative Works thereof, You may choose to
159offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160other liability obligations and/or rights consistent with this License. However,
161in accepting such obligations, You may act only on Your own behalf and on Your
162sole responsibility, not on behalf of any other Contributor, and only if You
163agree to indemnify, defend, and hold each Contributor harmless for any liability
164incurred by, or claims asserted against, such Contributor by reason of your
165accepting any such warranty or additional liability.
166
167END OF TERMS AND CONDITIONS
168
169APPENDIX: How to apply the Apache License to your work
170
171To apply the Apache License to your work, attach the following boilerplate
172notice, with the fields enclosed by brackets "[]" replaced with your own
173identifying information. (Don't include the brackets!) The text should be
174enclosed in the appropriate comment syntax for the file format. We also
175recommend that a file or class name and description of purpose be included on
176the same "printed page" as the copyright notice for easier identification within
177third-party archives.
178
179 Copyright 2014 Unknwon
180
181 Licensed under the Apache License, Version 2.0 (the "License");
182 you may not use this file except in compliance with the License.
183 You may obtain a copy of the License at
184
185 http://www.apache.org/licenses/LICENSE-2.0
186
187 Unless required by applicable law or agreed to in writing, software
188 distributed under the License is distributed on an "AS IS" BASIS,
189 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 See the License for the specific language governing permissions and
191 limitations under the License.
diff --git a/vendor/gopkg.in/ini.v1/Makefile b/vendor/gopkg.in/ini.v1/Makefile
deleted file mode 100644
index f3b0dae..0000000
--- a/vendor/gopkg.in/ini.v1/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
1.PHONY: build test bench vet coverage
2
3build: vet bench
4
5test:
6 go test -v -cover -race
7
8bench:
9 go test -v -cover -test.bench=. -test.benchmem
10
11vet:
12 go vet
13
14coverage:
15 go test -coverprofile=c.out && go tool cover -html=c.out && rm c.out
diff --git a/vendor/gopkg.in/ini.v1/README.md b/vendor/gopkg.in/ini.v1/README.md
deleted file mode 100644
index 30606d9..0000000
--- a/vendor/gopkg.in/ini.v1/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
1# INI
2
3[![GitHub Workflow Status](https://img.shields.io/github/checks-status/go-ini/ini/main?logo=github&style=for-the-badge)](https://github.com/go-ini/ini/actions?query=branch%3Amain)
4[![codecov](https://img.shields.io/codecov/c/github/go-ini/ini/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-ini/ini)
5[![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/github.com/go-ini/ini?tab=doc)
6[![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-ini/ini)
7
8![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
9
10Package ini provides INI file read and write functionality in Go.
11
12## Features
13
14- Load from multiple data sources(file, `[]byte`, `io.Reader` and `io.ReadCloser`) with overwrites.
15- Read with recursion values.
16- Read with parent-child sections.
17- Read with auto-increment key names.
18- Read with multiple-line values.
19- Read with tons of helper methods.
20- Read and convert values to Go types.
21- Read and **WRITE** comments of sections and keys.
22- Manipulate sections, keys and comments with ease.
23- Keep sections and keys in order as you parse and save.
24
25## Installation
26
27The minimum requirement of Go is **1.13**.
28
29```sh
30$ go get gopkg.in/ini.v1
31```
32
33Please add `-u` flag to update in the future.
34
35## Getting Help
36
37- [Getting Started](https://ini.unknwon.io/docs/intro/getting_started)
38- [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
39- 中国大陆镜像:https://ini.unknwon.cn
40
41## License
42
43This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.
diff --git a/vendor/gopkg.in/ini.v1/codecov.yml b/vendor/gopkg.in/ini.v1/codecov.yml
deleted file mode 100644
index e02ec84..0000000
--- a/vendor/gopkg.in/ini.v1/codecov.yml
+++ /dev/null
@@ -1,16 +0,0 @@
1coverage:
2 range: "60...95"
3 status:
4 project:
5 default:
6 threshold: 1%
7 informational: true
8 patch:
9 defualt:
10 only_pulls: true
11 informational: true
12
13comment:
14 layout: 'diff'
15
16github_checks: false
diff --git a/vendor/gopkg.in/ini.v1/data_source.go b/vendor/gopkg.in/ini.v1/data_source.go
deleted file mode 100644
index c3a541f..0000000
--- a/vendor/gopkg.in/ini.v1/data_source.go
+++ /dev/null
@@ -1,76 +0,0 @@
1// Copyright 2019 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17import (
18 "bytes"
19 "fmt"
20 "io"
21 "io/ioutil"
22 "os"
23)
24
25var (
26 _ dataSource = (*sourceFile)(nil)
27 _ dataSource = (*sourceData)(nil)
28 _ dataSource = (*sourceReadCloser)(nil)
29)
30
31// dataSource is an interface that returns object which can be read and closed.
32type dataSource interface {
33 ReadCloser() (io.ReadCloser, error)
34}
35
36// sourceFile represents an object that contains content on the local file system.
37type sourceFile struct {
38 name string
39}
40
41func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
42 return os.Open(s.name)
43}
44
45// sourceData represents an object that contains content in memory.
46type sourceData struct {
47 data []byte
48}
49
50func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
51 return ioutil.NopCloser(bytes.NewReader(s.data)), nil
52}
53
54// sourceReadCloser represents an input stream with Close method.
55type sourceReadCloser struct {
56 reader io.ReadCloser
57}
58
59func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
60 return s.reader, nil
61}
62
63func parseDataSource(source interface{}) (dataSource, error) {
64 switch s := source.(type) {
65 case string:
66 return sourceFile{s}, nil
67 case []byte:
68 return &sourceData{s}, nil
69 case io.ReadCloser:
70 return &sourceReadCloser{s}, nil
71 case io.Reader:
72 return &sourceReadCloser{ioutil.NopCloser(s)}, nil
73 default:
74 return nil, fmt.Errorf("error parsing data source: unknown type %q", s)
75 }
76}
diff --git a/vendor/gopkg.in/ini.v1/deprecated.go b/vendor/gopkg.in/ini.v1/deprecated.go
deleted file mode 100644
index 48b8e66..0000000
--- a/vendor/gopkg.in/ini.v1/deprecated.go
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2019 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17var (
18 // Deprecated: Use "DefaultSection" instead.
19 DEFAULT_SECTION = DefaultSection
20 // Deprecated: AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
21 AllCapsUnderscore = SnackCase
22)
diff --git a/vendor/gopkg.in/ini.v1/error.go b/vendor/gopkg.in/ini.v1/error.go
deleted file mode 100644
index f66bc94..0000000
--- a/vendor/gopkg.in/ini.v1/error.go
+++ /dev/null
@@ -1,49 +0,0 @@
1// Copyright 2016 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17import (
18 "fmt"
19)
20
21// ErrDelimiterNotFound indicates the error type of no delimiter is found which there should be one.
22type ErrDelimiterNotFound struct {
23 Line string
24}
25
26// IsErrDelimiterNotFound returns true if the given error is an instance of ErrDelimiterNotFound.
27func IsErrDelimiterNotFound(err error) bool {
28 _, ok := err.(ErrDelimiterNotFound)
29 return ok
30}
31
32func (err ErrDelimiterNotFound) Error() string {
33 return fmt.Sprintf("key-value delimiter not found: %s", err.Line)
34}
35
36// ErrEmptyKeyName indicates the error type of no key name is found which there should be one.
37type ErrEmptyKeyName struct {
38 Line string
39}
40
41// IsErrEmptyKeyName returns true if the given error is an instance of ErrEmptyKeyName.
42func IsErrEmptyKeyName(err error) bool {
43 _, ok := err.(ErrEmptyKeyName)
44 return ok
45}
46
47func (err ErrEmptyKeyName) Error() string {
48 return fmt.Sprintf("empty key name: %s", err.Line)
49}
diff --git a/vendor/gopkg.in/ini.v1/file.go b/vendor/gopkg.in/ini.v1/file.go
deleted file mode 100644
index f8b2240..0000000
--- a/vendor/gopkg.in/ini.v1/file.go
+++ /dev/null
@@ -1,541 +0,0 @@
1// Copyright 2017 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17import (
18 "bytes"
19 "errors"
20 "fmt"
21 "io"
22 "io/ioutil"
23 "os"
24 "strings"
25 "sync"
26)
27
28// File represents a combination of one or more INI files in memory.
29type File struct {
30 options LoadOptions
31 dataSources []dataSource
32
33 // Should make things safe, but sometimes doesn't matter.
34 BlockMode bool
35 lock sync.RWMutex
36
37 // To keep data in order.
38 sectionList []string
39 // To keep track of the index of a section with same name.
40 // This meta list is only used with non-unique section names are allowed.
41 sectionIndexes []int
42
43 // Actual data is stored here.
44 sections map[string][]*Section
45
46 NameMapper
47 ValueMapper
48}
49
50// newFile initializes File object with given data sources.
51func newFile(dataSources []dataSource, opts LoadOptions) *File {
52 if len(opts.KeyValueDelimiters) == 0 {
53 opts.KeyValueDelimiters = "=:"
54 }
55 if len(opts.KeyValueDelimiterOnWrite) == 0 {
56 opts.KeyValueDelimiterOnWrite = "="
57 }
58 if len(opts.ChildSectionDelimiter) == 0 {
59 opts.ChildSectionDelimiter = "."
60 }
61
62 return &File{
63 BlockMode: true,
64 dataSources: dataSources,
65 sections: make(map[string][]*Section),
66 options: opts,
67 }
68}
69
70// Empty returns an empty file object.
71func Empty(opts ...LoadOptions) *File {
72 var opt LoadOptions
73 if len(opts) > 0 {
74 opt = opts[0]
75 }
76
77 // Ignore error here, we are sure our data is good.
78 f, _ := LoadSources(opt, []byte(""))
79 return f
80}
81
82// NewSection creates a new section.
83func (f *File) NewSection(name string) (*Section, error) {
84 if len(name) == 0 {
85 return nil, errors.New("empty section name")
86 }
87
88 if (f.options.Insensitive || f.options.InsensitiveSections) && name != DefaultSection {
89 name = strings.ToLower(name)
90 }
91
92 if f.BlockMode {
93 f.lock.Lock()
94 defer f.lock.Unlock()
95 }
96
97 if !f.options.AllowNonUniqueSections && inSlice(name, f.sectionList) {
98 return f.sections[name][0], nil
99 }
100
101 f.sectionList = append(f.sectionList, name)
102
103 // NOTE: Append to indexes must happen before appending to sections,
104 // otherwise index will have off-by-one problem.
105 f.sectionIndexes = append(f.sectionIndexes, len(f.sections[name]))
106
107 sec := newSection(f, name)
108 f.sections[name] = append(f.sections[name], sec)
109
110 return sec, nil
111}
112
113// NewRawSection creates a new section with an unparseable body.
114func (f *File) NewRawSection(name, body string) (*Section, error) {
115 section, err := f.NewSection(name)
116 if err != nil {
117 return nil, err
118 }
119
120 section.isRawSection = true
121 section.rawBody = body
122 return section, nil
123}
124
125// NewSections creates a list of sections.
126func (f *File) NewSections(names ...string) (err error) {
127 for _, name := range names {
128 if _, err = f.NewSection(name); err != nil {
129 return err
130 }
131 }
132 return nil
133}
134
135// GetSection returns section by given name.
136func (f *File) GetSection(name string) (*Section, error) {
137 secs, err := f.SectionsByName(name)
138 if err != nil {
139 return nil, err
140 }
141
142 return secs[0], err
143}
144
145// HasSection returns true if the file contains a section with given name.
146func (f *File) HasSection(name string) bool {
147 section, _ := f.GetSection(name)
148 return section != nil
149}
150
151// SectionsByName returns all sections with given name.
152func (f *File) SectionsByName(name string) ([]*Section, error) {
153 if len(name) == 0 {
154 name = DefaultSection
155 }
156 if f.options.Insensitive || f.options.InsensitiveSections {
157 name = strings.ToLower(name)
158 }
159
160 if f.BlockMode {
161 f.lock.RLock()
162 defer f.lock.RUnlock()
163 }
164
165 secs := f.sections[name]
166 if len(secs) == 0 {
167 return nil, fmt.Errorf("section %q does not exist", name)
168 }
169
170 return secs, nil
171}
172
173// Section assumes named section exists and returns a zero-value when not.
174func (f *File) Section(name string) *Section {
175 sec, err := f.GetSection(name)
176 if err != nil {
177 if name == "" {
178 name = DefaultSection
179 }
180 sec, _ = f.NewSection(name)
181 return sec
182 }
183 return sec
184}
185
186// SectionWithIndex assumes named section exists and returns a new section when not.
187func (f *File) SectionWithIndex(name string, index int) *Section {
188 secs, err := f.SectionsByName(name)
189 if err != nil || len(secs) <= index {
190 // NOTE: It's OK here because the only possible error is empty section name,
191 // but if it's empty, this piece of code won't be executed.
192 newSec, _ := f.NewSection(name)
193 return newSec
194 }
195
196 return secs[index]
197}
198
199// Sections returns a list of Section stored in the current instance.
200func (f *File) Sections() []*Section {
201 if f.BlockMode {
202 f.lock.RLock()
203 defer f.lock.RUnlock()
204 }
205
206 sections := make([]*Section, len(f.sectionList))
207 for i, name := range f.sectionList {
208 sections[i] = f.sections[name][f.sectionIndexes[i]]
209 }
210 return sections
211}
212
213// ChildSections returns a list of child sections of given section name.
214func (f *File) ChildSections(name string) []*Section {
215 return f.Section(name).ChildSections()
216}
217
218// SectionStrings returns list of section names.
219func (f *File) SectionStrings() []string {
220 list := make([]string, len(f.sectionList))
221 copy(list, f.sectionList)
222 return list
223}
224
225// DeleteSection deletes a section or all sections with given name.
226func (f *File) DeleteSection(name string) {
227 secs, err := f.SectionsByName(name)
228 if err != nil {
229 return
230 }
231
232 for i := 0; i < len(secs); i++ {
233 // For non-unique sections, it is always needed to remove the first one so
234 // in the next iteration, the subsequent section continue having index 0.
235 // Ignoring the error as index 0 never returns an error.
236 _ = f.DeleteSectionWithIndex(name, 0)
237 }
238}
239
240// DeleteSectionWithIndex deletes a section with given name and index.
241func (f *File) DeleteSectionWithIndex(name string, index int) error {
242 if !f.options.AllowNonUniqueSections && index != 0 {
243 return fmt.Errorf("delete section with non-zero index is only allowed when non-unique sections is enabled")
244 }
245
246 if len(name) == 0 {
247 name = DefaultSection
248 }
249 if f.options.Insensitive || f.options.InsensitiveSections {
250 name = strings.ToLower(name)
251 }
252
253 if f.BlockMode {
254 f.lock.Lock()
255 defer f.lock.Unlock()
256 }
257
258 // Count occurrences of the sections
259 occurrences := 0
260
261 sectionListCopy := make([]string, len(f.sectionList))
262 copy(sectionListCopy, f.sectionList)
263
264 for i, s := range sectionListCopy {
265 if s != name {
266 continue
267 }
268
269 if occurrences == index {
270 if len(f.sections[name]) <= 1 {
271 delete(f.sections, name) // The last one in the map
272 } else {
273 f.sections[name] = append(f.sections[name][:index], f.sections[name][index+1:]...)
274 }
275
276 // Fix section lists
277 f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
278 f.sectionIndexes = append(f.sectionIndexes[:i], f.sectionIndexes[i+1:]...)
279
280 } else if occurrences > index {
281 // Fix the indices of all following sections with this name.
282 f.sectionIndexes[i-1]--
283 }
284
285 occurrences++
286 }
287
288 return nil
289}
290
291func (f *File) reload(s dataSource) error {
292 r, err := s.ReadCloser()
293 if err != nil {
294 return err
295 }
296 defer r.Close()
297
298 return f.parse(r)
299}
300
301// Reload reloads and parses all data sources.
302func (f *File) Reload() (err error) {
303 for _, s := range f.dataSources {
304 if err = f.reload(s); err != nil {
305 // In loose mode, we create an empty default section for nonexistent files.
306 if os.IsNotExist(err) && f.options.Loose {
307 _ = f.parse(bytes.NewBuffer(nil))
308 continue
309 }
310 return err
311 }
312 if f.options.ShortCircuit {
313 return nil
314 }
315 }
316 return nil
317}
318
319// Append appends one or more data sources and reloads automatically.
320func (f *File) Append(source interface{}, others ...interface{}) error {
321 ds, err := parseDataSource(source)
322 if err != nil {
323 return err
324 }
325 f.dataSources = append(f.dataSources, ds)
326 for _, s := range others {
327 ds, err = parseDataSource(s)
328 if err != nil {
329 return err
330 }
331 f.dataSources = append(f.dataSources, ds)
332 }
333 return f.Reload()
334}
335
336func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
337 equalSign := DefaultFormatLeft + f.options.KeyValueDelimiterOnWrite + DefaultFormatRight
338
339 if PrettyFormat || PrettyEqual {
340 equalSign = fmt.Sprintf(" %s ", f.options.KeyValueDelimiterOnWrite)
341 }
342
343 // Use buffer to make sure target is safe until finish encoding.
344 buf := bytes.NewBuffer(nil)
345 lastSectionIdx := len(f.sectionList) - 1
346 for i, sname := range f.sectionList {
347 sec := f.SectionWithIndex(sname, f.sectionIndexes[i])
348 if len(sec.Comment) > 0 {
349 // Support multiline comments
350 lines := strings.Split(sec.Comment, LineBreak)
351 for i := range lines {
352 if lines[i][0] != '#' && lines[i][0] != ';' {
353 lines[i] = "; " + lines[i]
354 } else {
355 lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:])
356 }
357
358 if _, err := buf.WriteString(lines[i] + LineBreak); err != nil {
359 return nil, err
360 }
361 }
362 }
363
364 if i > 0 || DefaultHeader || (i == 0 && strings.ToUpper(sec.name) != DefaultSection) {
365 if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
366 return nil, err
367 }
368 } else {
369 // Write nothing if default section is empty
370 if len(sec.keyList) == 0 {
371 continue
372 }
373 }
374
375 isLastSection := i == lastSectionIdx
376 if sec.isRawSection {
377 if _, err := buf.WriteString(sec.rawBody); err != nil {
378 return nil, err
379 }
380
381 if PrettySection && !isLastSection {
382 // Put a line between sections
383 if _, err := buf.WriteString(LineBreak); err != nil {
384 return nil, err
385 }
386 }
387 continue
388 }
389
390 // Count and generate alignment length and buffer spaces using the
391 // longest key. Keys may be modified if they contain certain characters so
392 // we need to take that into account in our calculation.
393 alignLength := 0
394 if PrettyFormat {
395 for _, kname := range sec.keyList {
396 keyLength := len(kname)
397 // First case will surround key by ` and second by """
398 if strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters) {
399 keyLength += 2
400 } else if strings.Contains(kname, "`") {
401 keyLength += 6
402 }
403
404 if keyLength > alignLength {
405 alignLength = keyLength
406 }
407 }
408 }
409 alignSpaces := bytes.Repeat([]byte(" "), alignLength)
410
411 KeyList:
412 for _, kname := range sec.keyList {
413 key := sec.Key(kname)
414 if len(key.Comment) > 0 {
415 if len(indent) > 0 && sname != DefaultSection {
416 buf.WriteString(indent)
417 }
418
419 // Support multiline comments
420 lines := strings.Split(key.Comment, LineBreak)
421 for i := range lines {
422 if lines[i][0] != '#' && lines[i][0] != ';' {
423 lines[i] = "; " + strings.TrimSpace(lines[i])
424 } else {
425 lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:])
426 }
427
428 if _, err := buf.WriteString(lines[i] + LineBreak); err != nil {
429 return nil, err
430 }
431 }
432 }
433
434 if len(indent) > 0 && sname != DefaultSection {
435 buf.WriteString(indent)
436 }
437
438 switch {
439 case key.isAutoIncrement:
440 kname = "-"
441 case strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters):
442 kname = "`" + kname + "`"
443 case strings.Contains(kname, "`"):
444 kname = `"""` + kname + `"""`
445 }
446
447 writeKeyValue := func(val string) (bool, error) {
448 if _, err := buf.WriteString(kname); err != nil {
449 return false, err
450 }
451
452 if key.isBooleanType {
453 buf.WriteString(LineBreak)
454 return true, nil
455 }
456
457 // Write out alignment spaces before "=" sign
458 if PrettyFormat {
459 buf.Write(alignSpaces[:alignLength-len(kname)])
460 }
461
462 // In case key value contains "\n", "`", "\"", "#" or ";"
463 if strings.ContainsAny(val, "\n`") {
464 val = `"""` + val + `"""`
465 } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
466 val = "`" + val + "`"
467 } else if len(strings.TrimSpace(val)) != len(val) {
468 val = `"` + val + `"`
469 }
470 if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
471 return false, err
472 }
473 return false, nil
474 }
475
476 shadows := key.ValueWithShadows()
477 if len(shadows) == 0 {
478 if _, err := writeKeyValue(""); err != nil {
479 return nil, err
480 }
481 }
482
483 for _, val := range shadows {
484 exitLoop, err := writeKeyValue(val)
485 if err != nil {
486 return nil, err
487 } else if exitLoop {
488 continue KeyList
489 }
490 }
491
492 for _, val := range key.nestedValues {
493 if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil {
494 return nil, err
495 }
496 }
497 }
498
499 if PrettySection && !isLastSection {
500 // Put a line between sections
501 if _, err := buf.WriteString(LineBreak); err != nil {
502 return nil, err
503 }
504 }
505 }
506
507 return buf, nil
508}
509
510// WriteToIndent writes content into io.Writer with given indention.
511// If PrettyFormat has been set to be true,
512// it will align "=" sign with spaces under each section.
513func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) {
514 buf, err := f.writeToBuffer(indent)
515 if err != nil {
516 return 0, err
517 }
518 return buf.WriteTo(w)
519}
520
521// WriteTo writes file content into io.Writer.
522func (f *File) WriteTo(w io.Writer) (int64, error) {
523 return f.WriteToIndent(w, "")
524}
525
526// SaveToIndent writes content to file system with given value indention.
527func (f *File) SaveToIndent(filename, indent string) error {
528 // Note: Because we are truncating with os.Create,
529 // so it's safer to save to a temporary file location and rename after done.
530 buf, err := f.writeToBuffer(indent)
531 if err != nil {
532 return err
533 }
534
535 return ioutil.WriteFile(filename, buf.Bytes(), 0666)
536}
537
538// SaveTo writes content to file system.
539func (f *File) SaveTo(filename string) error {
540 return f.SaveToIndent(filename, "")
541}
diff --git a/vendor/gopkg.in/ini.v1/helper.go b/vendor/gopkg.in/ini.v1/helper.go
deleted file mode 100644
index f9d80a6..0000000
--- a/vendor/gopkg.in/ini.v1/helper.go
+++ /dev/null
@@ -1,24 +0,0 @@
1// Copyright 2019 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17func inSlice(str string, s []string) bool {
18 for _, v := range s {
19 if str == v {
20 return true
21 }
22 }
23 return false
24}
diff --git a/vendor/gopkg.in/ini.v1/ini.go b/vendor/gopkg.in/ini.v1/ini.go
deleted file mode 100644
index 99e7f86..0000000
--- a/vendor/gopkg.in/ini.v1/ini.go
+++ /dev/null
@@ -1,176 +0,0 @@
1// Copyright 2014 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15// Package ini provides INI file read and write functionality in Go.
16package ini
17
18import (
19 "os"
20 "regexp"
21 "runtime"
22 "strings"
23)
24
25const (
26 // Maximum allowed depth when recursively substituing variable names.
27 depthValues = 99
28)
29
30var (
31 // DefaultSection is the name of default section. You can use this var or the string literal.
32 // In most of cases, an empty string is all you need to access the section.
33 DefaultSection = "DEFAULT"
34
35 // LineBreak is the delimiter to determine or compose a new line.
36 // This variable will be changed to "\r\n" automatically on Windows at package init time.
37 LineBreak = "\n"
38
39 // Variable regexp pattern: %(variable)s
40 varPattern = regexp.MustCompile(`%\(([^)]+)\)s`)
41
42 // DefaultHeader explicitly writes default section header.
43 DefaultHeader = false
44
45 // PrettySection indicates whether to put a line between sections.
46 PrettySection = true
47 // PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output
48 // or reduce all possible spaces for compact format.
49 PrettyFormat = true
50 // PrettyEqual places spaces around "=" sign even when PrettyFormat is false.
51 PrettyEqual = false
52 // DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled.
53 DefaultFormatLeft = ""
54 // DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled.
55 DefaultFormatRight = ""
56)
57
58var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test")
59
60func init() {
61 if runtime.GOOS == "windows" && !inTest {
62 LineBreak = "\r\n"
63 }
64}
65
66// LoadOptions contains all customized options used for load data source(s).
67type LoadOptions struct {
68 // Loose indicates whether the parser should ignore nonexistent files or return error.
69 Loose bool
70 // Insensitive indicates whether the parser forces all section and key names to lowercase.
71 Insensitive bool
72 // InsensitiveSections indicates whether the parser forces all section to lowercase.
73 InsensitiveSections bool
74 // InsensitiveKeys indicates whether the parser forces all key names to lowercase.
75 InsensitiveKeys bool
76 // IgnoreContinuation indicates whether to ignore continuation lines while parsing.
77 IgnoreContinuation bool
78 // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
79 IgnoreInlineComment bool
80 // SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs.
81 SkipUnrecognizableLines bool
82 // ShortCircuit indicates whether to ignore other configuration sources after loaded the first available configuration source.
83 ShortCircuit bool
84 // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
85 // This type of keys are mostly used in my.cnf.
86 AllowBooleanKeys bool
87 // AllowShadows indicates whether to keep track of keys with same name under same section.
88 AllowShadows bool
89 // AllowNestedValues indicates whether to allow AWS-like nested values.
90 // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values
91 AllowNestedValues bool
92 // AllowPythonMultilineValues indicates whether to allow Python-like multi-line values.
93 // Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure
94 // Relevant quote: Values can also span multiple lines, as long as they are indented deeper
95 // than the first line of the value.
96 AllowPythonMultilineValues bool
97 // SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value.
98 // Docs: https://docs.python.org/2/library/configparser.html
99 // Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names.
100 // In the latter case, they need to be preceded by a whitespace character to be recognized as a comment.
101 SpaceBeforeInlineComment bool
102 // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format
103 // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
104 UnescapeValueDoubleQuotes bool
105 // UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format
106 // when value is NOT surrounded by any quotes.
107 // Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all.
108 UnescapeValueCommentSymbols bool
109 // UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise
110 // conform to key/value pairs. Specify the names of those blocks here.
111 UnparseableSections []string
112 // KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:".
113 KeyValueDelimiters string
114 // KeyValueDelimiterOnWrite is the delimiter that are used to separate key and value output. By default, it is "=".
115 KeyValueDelimiterOnWrite string
116 // ChildSectionDelimiter is the delimiter that is used to separate child sections. By default, it is ".".
117 ChildSectionDelimiter string
118 // PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes).
119 PreserveSurroundedQuote bool
120 // DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values).
121 DebugFunc DebugFunc
122 // ReaderBufferSize is the buffer size of the reader in bytes.
123 ReaderBufferSize int
124 // AllowNonUniqueSections indicates whether to allow sections with the same name multiple times.
125 AllowNonUniqueSections bool
126 // AllowDuplicateShadowValues indicates whether values for shadowed keys should be deduplicated.
127 AllowDuplicateShadowValues bool
128}
129
130// DebugFunc is the type of function called to log parse events.
131type DebugFunc func(message string)
132
133// LoadSources allows caller to apply customized options for loading from data source(s).
134func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
135 sources := make([]dataSource, len(others)+1)
136 sources[0], err = parseDataSource(source)
137 if err != nil {
138 return nil, err
139 }
140 for i := range others {
141 sources[i+1], err = parseDataSource(others[i])
142 if err != nil {
143 return nil, err
144 }
145 }
146 f := newFile(sources, opts)
147 if err = f.Reload(); err != nil {
148 return nil, err
149 }
150 return f, nil
151}
152
153// Load loads and parses from INI data sources.
154// Arguments can be mixed of file name with string type, or raw data in []byte.
155// It will return error if list contains nonexistent files.
156func Load(source interface{}, others ...interface{}) (*File, error) {
157 return LoadSources(LoadOptions{}, source, others...)
158}
159
160// LooseLoad has exactly same functionality as Load function
161// except it ignores nonexistent files instead of returning error.
162func LooseLoad(source interface{}, others ...interface{}) (*File, error) {
163 return LoadSources(LoadOptions{Loose: true}, source, others...)
164}
165
166// InsensitiveLoad has exactly same functionality as Load function
167// except it forces all section and key names to be lowercased.
168func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
169 return LoadSources(LoadOptions{Insensitive: true}, source, others...)
170}
171
172// ShadowLoad has exactly same functionality as Load function
173// except it allows have shadow keys.
174func ShadowLoad(source interface{}, others ...interface{}) (*File, error) {
175 return LoadSources(LoadOptions{AllowShadows: true}, source, others...)
176}
diff --git a/vendor/gopkg.in/ini.v1/key.go b/vendor/gopkg.in/ini.v1/key.go
deleted file mode 100644
index a19d9f3..0000000
--- a/vendor/gopkg.in/ini.v1/key.go
+++ /dev/null
@@ -1,837 +0,0 @@
1// Copyright 2014 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17import (
18 "bytes"
19 "errors"
20 "fmt"
21 "strconv"
22 "strings"
23 "time"
24)
25
26// Key represents a key under a section.
27type Key struct {
28 s *Section
29 Comment string
30 name string
31 value string
32 isAutoIncrement bool
33 isBooleanType bool
34
35 isShadow bool
36 shadows []*Key
37
38 nestedValues []string
39}
40
41// newKey simply return a key object with given values.
42func newKey(s *Section, name, val string) *Key {
43 return &Key{
44 s: s,
45 name: name,
46 value: val,
47 }
48}
49
50func (k *Key) addShadow(val string) error {
51 if k.isShadow {
52 return errors.New("cannot add shadow to another shadow key")
53 } else if k.isAutoIncrement || k.isBooleanType {
54 return errors.New("cannot add shadow to auto-increment or boolean key")
55 }
56
57 if !k.s.f.options.AllowDuplicateShadowValues {
58 // Deduplicate shadows based on their values.
59 if k.value == val {
60 return nil
61 }
62 for i := range k.shadows {
63 if k.shadows[i].value == val {
64 return nil
65 }
66 }
67 }
68
69 shadow := newKey(k.s, k.name, val)
70 shadow.isShadow = true
71 k.shadows = append(k.shadows, shadow)
72 return nil
73}
74
75// AddShadow adds a new shadow key to itself.
76func (k *Key) AddShadow(val string) error {
77 if !k.s.f.options.AllowShadows {
78 return errors.New("shadow key is not allowed")
79 }
80 return k.addShadow(val)
81}
82
83func (k *Key) addNestedValue(val string) error {
84 if k.isAutoIncrement || k.isBooleanType {
85 return errors.New("cannot add nested value to auto-increment or boolean key")
86 }
87
88 k.nestedValues = append(k.nestedValues, val)
89 return nil
90}
91
92// AddNestedValue adds a nested value to the key.
93func (k *Key) AddNestedValue(val string) error {
94 if !k.s.f.options.AllowNestedValues {
95 return errors.New("nested value is not allowed")
96 }
97 return k.addNestedValue(val)
98}
99
100// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv
101type ValueMapper func(string) string
102
103// Name returns name of key.
104func (k *Key) Name() string {
105 return k.name
106}
107
108// Value returns raw value of key for performance purpose.
109func (k *Key) Value() string {
110 return k.value
111}
112
113// ValueWithShadows returns raw values of key and its shadows if any. Shadow
114// keys with empty values are ignored from the returned list.
115func (k *Key) ValueWithShadows() []string {
116 if len(k.shadows) == 0 {
117 if k.value == "" {
118 return []string{}
119 }
120 return []string{k.value}
121 }
122
123 vals := make([]string, 0, len(k.shadows)+1)
124 if k.value != "" {
125 vals = append(vals, k.value)
126 }
127 for _, s := range k.shadows {
128 if s.value != "" {
129 vals = append(vals, s.value)
130 }
131 }
132 return vals
133}
134
135// NestedValues returns nested values stored in the key.
136// It is possible returned value is nil if no nested values stored in the key.
137func (k *Key) NestedValues() []string {
138 return k.nestedValues
139}
140
141// transformValue takes a raw value and transforms to its final string.
142func (k *Key) transformValue(val string) string {
143 if k.s.f.ValueMapper != nil {
144 val = k.s.f.ValueMapper(val)
145 }
146
147 // Fail-fast if no indicate char found for recursive value
148 if !strings.Contains(val, "%") {
149 return val
150 }
151 for i := 0; i < depthValues; i++ {
152 vr := varPattern.FindString(val)
153 if len(vr) == 0 {
154 break
155 }
156
157 // Take off leading '%(' and trailing ')s'.
158 noption := vr[2 : len(vr)-2]
159
160 // Search in the same section.
161 // If not found or found the key itself, then search again in default section.
162 nk, err := k.s.GetKey(noption)
163 if err != nil || k == nk {
164 nk, _ = k.s.f.Section("").GetKey(noption)
165 if nk == nil {
166 // Stop when no results found in the default section,
167 // and returns the value as-is.
168 break
169 }
170 }
171
172 // Substitute by new value and take off leading '%(' and trailing ')s'.
173 val = strings.Replace(val, vr, nk.value, -1)
174 }
175 return val
176}
177
178// String returns string representation of value.
179func (k *Key) String() string {
180 return k.transformValue(k.value)
181}
182
183// Validate accepts a validate function which can
184// return modifed result as key value.
185func (k *Key) Validate(fn func(string) string) string {
186 return fn(k.String())
187}
188
189// parseBool returns the boolean value represented by the string.
190//
191// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On,
192// 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off.
193// Any other value returns an error.
194func parseBool(str string) (value bool, err error) {
195 switch str {
196 case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On":
197 return true, nil
198 case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off":
199 return false, nil
200 }
201 return false, fmt.Errorf("parsing \"%s\": invalid syntax", str)
202}
203
204// Bool returns bool type value.
205func (k *Key) Bool() (bool, error) {
206 return parseBool(k.String())
207}
208
209// Float64 returns float64 type value.
210func (k *Key) Float64() (float64, error) {
211 return strconv.ParseFloat(k.String(), 64)
212}
213
214// Int returns int type value.
215func (k *Key) Int() (int, error) {
216 v, err := strconv.ParseInt(k.String(), 0, 64)
217 return int(v), err
218}
219
220// Int64 returns int64 type value.
221func (k *Key) Int64() (int64, error) {
222 return strconv.ParseInt(k.String(), 0, 64)
223}
224
225// Uint returns uint type valued.
226func (k *Key) Uint() (uint, error) {
227 u, e := strconv.ParseUint(k.String(), 0, 64)
228 return uint(u), e
229}
230
231// Uint64 returns uint64 type value.
232func (k *Key) Uint64() (uint64, error) {
233 return strconv.ParseUint(k.String(), 0, 64)
234}
235
236// Duration returns time.Duration type value.
237func (k *Key) Duration() (time.Duration, error) {
238 return time.ParseDuration(k.String())
239}
240
241// TimeFormat parses with given format and returns time.Time type value.
242func (k *Key) TimeFormat(format string) (time.Time, error) {
243 return time.Parse(format, k.String())
244}
245
246// Time parses with RFC3339 format and returns time.Time type value.
247func (k *Key) Time() (time.Time, error) {
248 return k.TimeFormat(time.RFC3339)
249}
250
251// MustString returns default value if key value is empty.
252func (k *Key) MustString(defaultVal string) string {
253 val := k.String()
254 if len(val) == 0 {
255 k.value = defaultVal
256 return defaultVal
257 }
258 return val
259}
260
261// MustBool always returns value without error,
262// it returns false if error occurs.
263func (k *Key) MustBool(defaultVal ...bool) bool {
264 val, err := k.Bool()
265 if len(defaultVal) > 0 && err != nil {
266 k.value = strconv.FormatBool(defaultVal[0])
267 return defaultVal[0]
268 }
269 return val
270}
271
272// MustFloat64 always returns value without error,
273// it returns 0.0 if error occurs.
274func (k *Key) MustFloat64(defaultVal ...float64) float64 {
275 val, err := k.Float64()
276 if len(defaultVal) > 0 && err != nil {
277 k.value = strconv.FormatFloat(defaultVal[0], 'f', -1, 64)
278 return defaultVal[0]
279 }
280 return val
281}
282
283// MustInt always returns value without error,
284// it returns 0 if error occurs.
285func (k *Key) MustInt(defaultVal ...int) int {
286 val, err := k.Int()
287 if len(defaultVal) > 0 && err != nil {
288 k.value = strconv.FormatInt(int64(defaultVal[0]), 10)
289 return defaultVal[0]
290 }
291 return val
292}
293
294// MustInt64 always returns value without error,
295// it returns 0 if error occurs.
296func (k *Key) MustInt64(defaultVal ...int64) int64 {
297 val, err := k.Int64()
298 if len(defaultVal) > 0 && err != nil {
299 k.value = strconv.FormatInt(defaultVal[0], 10)
300 return defaultVal[0]
301 }
302 return val
303}
304
305// MustUint always returns value without error,
306// it returns 0 if error occurs.
307func (k *Key) MustUint(defaultVal ...uint) uint {
308 val, err := k.Uint()
309 if len(defaultVal) > 0 && err != nil {
310 k.value = strconv.FormatUint(uint64(defaultVal[0]), 10)
311 return defaultVal[0]
312 }
313 return val
314}
315
316// MustUint64 always returns value without error,
317// it returns 0 if error occurs.
318func (k *Key) MustUint64(defaultVal ...uint64) uint64 {
319 val, err := k.Uint64()
320 if len(defaultVal) > 0 && err != nil {
321 k.value = strconv.FormatUint(defaultVal[0], 10)
322 return defaultVal[0]
323 }
324 return val
325}
326
327// MustDuration always returns value without error,
328// it returns zero value if error occurs.
329func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration {
330 val, err := k.Duration()
331 if len(defaultVal) > 0 && err != nil {
332 k.value = defaultVal[0].String()
333 return defaultVal[0]
334 }
335 return val
336}
337
338// MustTimeFormat always parses with given format and returns value without error,
339// it returns zero value if error occurs.
340func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time {
341 val, err := k.TimeFormat(format)
342 if len(defaultVal) > 0 && err != nil {
343 k.value = defaultVal[0].Format(format)
344 return defaultVal[0]
345 }
346 return val
347}
348
349// MustTime always parses with RFC3339 format and returns value without error,
350// it returns zero value if error occurs.
351func (k *Key) MustTime(defaultVal ...time.Time) time.Time {
352 return k.MustTimeFormat(time.RFC3339, defaultVal...)
353}
354
355// In always returns value without error,
356// it returns default value if error occurs or doesn't fit into candidates.
357func (k *Key) In(defaultVal string, candidates []string) string {
358 val := k.String()
359 for _, cand := range candidates {
360 if val == cand {
361 return val
362 }
363 }
364 return defaultVal
365}
366
367// InFloat64 always returns value without error,
368// it returns default value if error occurs or doesn't fit into candidates.
369func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 {
370 val := k.MustFloat64()
371 for _, cand := range candidates {
372 if val == cand {
373 return val
374 }
375 }
376 return defaultVal
377}
378
379// InInt always returns value without error,
380// it returns default value if error occurs or doesn't fit into candidates.
381func (k *Key) InInt(defaultVal int, candidates []int) int {
382 val := k.MustInt()
383 for _, cand := range candidates {
384 if val == cand {
385 return val
386 }
387 }
388 return defaultVal
389}
390
391// InInt64 always returns value without error,
392// it returns default value if error occurs or doesn't fit into candidates.
393func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 {
394 val := k.MustInt64()
395 for _, cand := range candidates {
396 if val == cand {
397 return val
398 }
399 }
400 return defaultVal
401}
402
403// InUint always returns value without error,
404// it returns default value if error occurs or doesn't fit into candidates.
405func (k *Key) InUint(defaultVal uint, candidates []uint) uint {
406 val := k.MustUint()
407 for _, cand := range candidates {
408 if val == cand {
409 return val
410 }
411 }
412 return defaultVal
413}
414
415// InUint64 always returns value without error,
416// it returns default value if error occurs or doesn't fit into candidates.
417func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 {
418 val := k.MustUint64()
419 for _, cand := range candidates {
420 if val == cand {
421 return val
422 }
423 }
424 return defaultVal
425}
426
427// InTimeFormat always parses with given format and returns value without error,
428// it returns default value if error occurs or doesn't fit into candidates.
429func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time {
430 val := k.MustTimeFormat(format)
431 for _, cand := range candidates {
432 if val == cand {
433 return val
434 }
435 }
436 return defaultVal
437}
438
439// InTime always parses with RFC3339 format and returns value without error,
440// it returns default value if error occurs or doesn't fit into candidates.
441func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time {
442 return k.InTimeFormat(time.RFC3339, defaultVal, candidates)
443}
444
445// RangeFloat64 checks if value is in given range inclusively,
446// and returns default value if it's not.
447func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 {
448 val := k.MustFloat64()
449 if val < min || val > max {
450 return defaultVal
451 }
452 return val
453}
454
455// RangeInt checks if value is in given range inclusively,
456// and returns default value if it's not.
457func (k *Key) RangeInt(defaultVal, min, max int) int {
458 val := k.MustInt()
459 if val < min || val > max {
460 return defaultVal
461 }
462 return val
463}
464
465// RangeInt64 checks if value is in given range inclusively,
466// and returns default value if it's not.
467func (k *Key) RangeInt64(defaultVal, min, max int64) int64 {
468 val := k.MustInt64()
469 if val < min || val > max {
470 return defaultVal
471 }
472 return val
473}
474
475// RangeTimeFormat checks if value with given format is in given range inclusively,
476// and returns default value if it's not.
477func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time {
478 val := k.MustTimeFormat(format)
479 if val.Unix() < min.Unix() || val.Unix() > max.Unix() {
480 return defaultVal
481 }
482 return val
483}
484
485// RangeTime checks if value with RFC3339 format is in given range inclusively,
486// and returns default value if it's not.
487func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time {
488 return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max)
489}
490
491// Strings returns list of string divided by given delimiter.
492func (k *Key) Strings(delim string) []string {
493 str := k.String()
494 if len(str) == 0 {
495 return []string{}
496 }
497
498 runes := []rune(str)
499 vals := make([]string, 0, 2)
500 var buf bytes.Buffer
501 escape := false
502 idx := 0
503 for {
504 if escape {
505 escape = false
506 if runes[idx] != '\\' && !strings.HasPrefix(string(runes[idx:]), delim) {
507 buf.WriteRune('\\')
508 }
509 buf.WriteRune(runes[idx])
510 } else {
511 if runes[idx] == '\\' {
512 escape = true
513 } else if strings.HasPrefix(string(runes[idx:]), delim) {
514 idx += len(delim) - 1
515 vals = append(vals, strings.TrimSpace(buf.String()))
516 buf.Reset()
517 } else {
518 buf.WriteRune(runes[idx])
519 }
520 }
521 idx++
522 if idx == len(runes) {
523 break
524 }
525 }
526
527 if buf.Len() > 0 {
528 vals = append(vals, strings.TrimSpace(buf.String()))
529 }
530
531 return vals
532}
533
534// StringsWithShadows returns list of string divided by given delimiter.
535// Shadows will also be appended if any.
536func (k *Key) StringsWithShadows(delim string) []string {
537 vals := k.ValueWithShadows()
538 results := make([]string, 0, len(vals)*2)
539 for i := range vals {
540 if len(vals) == 0 {
541 continue
542 }
543
544 results = append(results, strings.Split(vals[i], delim)...)
545 }
546
547 for i := range results {
548 results[i] = k.transformValue(strings.TrimSpace(results[i]))
549 }
550 return results
551}
552
553// Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value.
554func (k *Key) Float64s(delim string) []float64 {
555 vals, _ := k.parseFloat64s(k.Strings(delim), true, false)
556 return vals
557}
558
559// Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value.
560func (k *Key) Ints(delim string) []int {
561 vals, _ := k.parseInts(k.Strings(delim), true, false)
562 return vals
563}
564
565// Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value.
566func (k *Key) Int64s(delim string) []int64 {
567 vals, _ := k.parseInt64s(k.Strings(delim), true, false)
568 return vals
569}
570
571// Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value.
572func (k *Key) Uints(delim string) []uint {
573 vals, _ := k.parseUints(k.Strings(delim), true, false)
574 return vals
575}
576
577// Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value.
578func (k *Key) Uint64s(delim string) []uint64 {
579 vals, _ := k.parseUint64s(k.Strings(delim), true, false)
580 return vals
581}
582
583// Bools returns list of bool divided by given delimiter. Any invalid input will be treated as zero value.
584func (k *Key) Bools(delim string) []bool {
585 vals, _ := k.parseBools(k.Strings(delim), true, false)
586 return vals
587}
588
589// TimesFormat parses with given format and returns list of time.Time divided by given delimiter.
590// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
591func (k *Key) TimesFormat(format, delim string) []time.Time {
592 vals, _ := k.parseTimesFormat(format, k.Strings(delim), true, false)
593 return vals
594}
595
596// Times parses with RFC3339 format and returns list of time.Time divided by given delimiter.
597// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
598func (k *Key) Times(delim string) []time.Time {
599 return k.TimesFormat(time.RFC3339, delim)
600}
601
602// ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then
603// it will not be included to result list.
604func (k *Key) ValidFloat64s(delim string) []float64 {
605 vals, _ := k.parseFloat64s(k.Strings(delim), false, false)
606 return vals
607}
608
609// ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will
610// not be included to result list.
611func (k *Key) ValidInts(delim string) []int {
612 vals, _ := k.parseInts(k.Strings(delim), false, false)
613 return vals
614}
615
616// ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer,
617// then it will not be included to result list.
618func (k *Key) ValidInt64s(delim string) []int64 {
619 vals, _ := k.parseInt64s(k.Strings(delim), false, false)
620 return vals
621}
622
623// ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer,
624// then it will not be included to result list.
625func (k *Key) ValidUints(delim string) []uint {
626 vals, _ := k.parseUints(k.Strings(delim), false, false)
627 return vals
628}
629
630// ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned
631// integer, then it will not be included to result list.
632func (k *Key) ValidUint64s(delim string) []uint64 {
633 vals, _ := k.parseUint64s(k.Strings(delim), false, false)
634 return vals
635}
636
637// ValidBools returns list of bool divided by given delimiter. If some value is not 64-bit unsigned
638// integer, then it will not be included to result list.
639func (k *Key) ValidBools(delim string) []bool {
640 vals, _ := k.parseBools(k.Strings(delim), false, false)
641 return vals
642}
643
644// ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter.
645func (k *Key) ValidTimesFormat(format, delim string) []time.Time {
646 vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false)
647 return vals
648}
649
650// ValidTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter.
651func (k *Key) ValidTimes(delim string) []time.Time {
652 return k.ValidTimesFormat(time.RFC3339, delim)
653}
654
655// StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input.
656func (k *Key) StrictFloat64s(delim string) ([]float64, error) {
657 return k.parseFloat64s(k.Strings(delim), false, true)
658}
659
660// StrictInts returns list of int divided by given delimiter or error on first invalid input.
661func (k *Key) StrictInts(delim string) ([]int, error) {
662 return k.parseInts(k.Strings(delim), false, true)
663}
664
665// StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input.
666func (k *Key) StrictInt64s(delim string) ([]int64, error) {
667 return k.parseInt64s(k.Strings(delim), false, true)
668}
669
670// StrictUints returns list of uint divided by given delimiter or error on first invalid input.
671func (k *Key) StrictUints(delim string) ([]uint, error) {
672 return k.parseUints(k.Strings(delim), false, true)
673}
674
675// StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input.
676func (k *Key) StrictUint64s(delim string) ([]uint64, error) {
677 return k.parseUint64s(k.Strings(delim), false, true)
678}
679
680// StrictBools returns list of bool divided by given delimiter or error on first invalid input.
681func (k *Key) StrictBools(delim string) ([]bool, error) {
682 return k.parseBools(k.Strings(delim), false, true)
683}
684
685// StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter
686// or error on first invalid input.
687func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) {
688 return k.parseTimesFormat(format, k.Strings(delim), false, true)
689}
690
691// StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter
692// or error on first invalid input.
693func (k *Key) StrictTimes(delim string) ([]time.Time, error) {
694 return k.StrictTimesFormat(time.RFC3339, delim)
695}
696
697// parseBools transforms strings to bools.
698func (k *Key) parseBools(strs []string, addInvalid, returnOnInvalid bool) ([]bool, error) {
699 vals := make([]bool, 0, len(strs))
700 parser := func(str string) (interface{}, error) {
701 val, err := parseBool(str)
702 return val, err
703 }
704 rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
705 if err == nil {
706 for _, val := range rawVals {
707 vals = append(vals, val.(bool))
708 }
709 }
710 return vals, err
711}
712
713// parseFloat64s transforms strings to float64s.
714func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) {
715 vals := make([]float64, 0, len(strs))
716 parser := func(str string) (interface{}, error) {
717 val, err := strconv.ParseFloat(str, 64)
718 return val, err
719 }
720 rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
721 if err == nil {
722 for _, val := range rawVals {
723 vals = append(vals, val.(float64))
724 }
725 }
726 return vals, err
727}
728
729// parseInts transforms strings to ints.
730func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) {
731 vals := make([]int, 0, len(strs))
732 parser := func(str string) (interface{}, error) {
733 val, err := strconv.ParseInt(str, 0, 64)
734 return val, err
735 }
736 rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
737 if err == nil {
738 for _, val := range rawVals {
739 vals = append(vals, int(val.(int64)))
740 }
741 }
742 return vals, err
743}
744
745// parseInt64s transforms strings to int64s.
746func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) {
747 vals := make([]int64, 0, len(strs))
748 parser := func(str string) (interface{}, error) {
749 val, err := strconv.ParseInt(str, 0, 64)
750 return val, err
751 }
752
753 rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
754 if err == nil {
755 for _, val := range rawVals {
756 vals = append(vals, val.(int64))
757 }
758 }
759 return vals, err
760}
761
762// parseUints transforms strings to uints.
763func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) {
764 vals := make([]uint, 0, len(strs))
765 parser := func(str string) (interface{}, error) {
766 val, err := strconv.ParseUint(str, 0, 64)
767 return val, err
768 }
769
770 rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
771 if err == nil {
772 for _, val := range rawVals {
773 vals = append(vals, uint(val.(uint64)))
774 }
775 }
776 return vals, err
777}
778
779// parseUint64s transforms strings to uint64s.
780func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) {
781 vals := make([]uint64, 0, len(strs))
782 parser := func(str string) (interface{}, error) {
783 val, err := strconv.ParseUint(str, 0, 64)
784 return val, err
785 }
786 rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
787 if err == nil {
788 for _, val := range rawVals {
789 vals = append(vals, val.(uint64))
790 }
791 }
792 return vals, err
793}
794
795type Parser func(str string) (interface{}, error)
796
797// parseTimesFormat transforms strings to times in given format.
798func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) {
799 vals := make([]time.Time, 0, len(strs))
800 parser := func(str string) (interface{}, error) {
801 val, err := time.Parse(format, str)
802 return val, err
803 }
804 rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
805 if err == nil {
806 for _, val := range rawVals {
807 vals = append(vals, val.(time.Time))
808 }
809 }
810 return vals, err
811}
812
813// doParse transforms strings to different types
814func (k *Key) doParse(strs []string, addInvalid, returnOnInvalid bool, parser Parser) ([]interface{}, error) {
815 vals := make([]interface{}, 0, len(strs))
816 for _, str := range strs {
817 val, err := parser(str)
818 if err != nil && returnOnInvalid {
819 return nil, err
820 }
821 if err == nil || addInvalid {
822 vals = append(vals, val)
823 }
824 }
825 return vals, nil
826}
827
828// SetValue changes key value.
829func (k *Key) SetValue(v string) {
830 if k.s.f.BlockMode {
831 k.s.f.lock.Lock()
832 defer k.s.f.lock.Unlock()
833 }
834
835 k.value = v
836 k.s.keysHash[k.name] = v
837}
diff --git a/vendor/gopkg.in/ini.v1/parser.go b/vendor/gopkg.in/ini.v1/parser.go
deleted file mode 100644
index 44fc526..0000000
--- a/vendor/gopkg.in/ini.v1/parser.go
+++ /dev/null
@@ -1,520 +0,0 @@
1// Copyright 2015 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17import (
18 "bufio"
19 "bytes"
20 "fmt"
21 "io"
22 "regexp"
23 "strconv"
24 "strings"
25 "unicode"
26)
27
28const minReaderBufferSize = 4096
29
30var pythonMultiline = regexp.MustCompile(`^([\t\f ]+)(.*)`)
31
32type parserOptions struct {
33 IgnoreContinuation bool
34 IgnoreInlineComment bool
35 AllowPythonMultilineValues bool
36 SpaceBeforeInlineComment bool
37 UnescapeValueDoubleQuotes bool
38 UnescapeValueCommentSymbols bool
39 PreserveSurroundedQuote bool
40 DebugFunc DebugFunc
41 ReaderBufferSize int
42}
43
44type parser struct {
45 buf *bufio.Reader
46 options parserOptions
47
48 isEOF bool
49 count int
50 comment *bytes.Buffer
51}
52
53func (p *parser) debug(format string, args ...interface{}) {
54 if p.options.DebugFunc != nil {
55 p.options.DebugFunc(fmt.Sprintf(format, args...))
56 }
57}
58
59func newParser(r io.Reader, opts parserOptions) *parser {
60 size := opts.ReaderBufferSize
61 if size < minReaderBufferSize {
62 size = minReaderBufferSize
63 }
64
65 return &parser{
66 buf: bufio.NewReaderSize(r, size),
67 options: opts,
68 count: 1,
69 comment: &bytes.Buffer{},
70 }
71}
72
73// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format.
74// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
75func (p *parser) BOM() error {
76 mask, err := p.buf.Peek(2)
77 if err != nil && err != io.EOF {
78 return err
79 } else if len(mask) < 2 {
80 return nil
81 }
82
83 switch {
84 case mask[0] == 254 && mask[1] == 255:
85 fallthrough
86 case mask[0] == 255 && mask[1] == 254:
87 _, err = p.buf.Read(mask)
88 if err != nil {
89 return err
90 }
91 case mask[0] == 239 && mask[1] == 187:
92 mask, err := p.buf.Peek(3)
93 if err != nil && err != io.EOF {
94 return err
95 } else if len(mask) < 3 {
96 return nil
97 }
98 if mask[2] == 191 {
99 _, err = p.buf.Read(mask)
100 if err != nil {
101 return err
102 }
103 }
104 }
105 return nil
106}
107
108func (p *parser) readUntil(delim byte) ([]byte, error) {
109 data, err := p.buf.ReadBytes(delim)
110 if err != nil {
111 if err == io.EOF {
112 p.isEOF = true
113 } else {
114 return nil, err
115 }
116 }
117 return data, nil
118}
119
120func cleanComment(in []byte) ([]byte, bool) {
121 i := bytes.IndexAny(in, "#;")
122 if i == -1 {
123 return nil, false
124 }
125 return in[i:], true
126}
127
128func readKeyName(delimiters string, in []byte) (string, int, error) {
129 line := string(in)
130
131 // Check if key name surrounded by quotes.
132 var keyQuote string
133 if line[0] == '"' {
134 if len(line) > 6 && line[0:3] == `"""` {
135 keyQuote = `"""`
136 } else {
137 keyQuote = `"`
138 }
139 } else if line[0] == '`' {
140 keyQuote = "`"
141 }
142
143 // Get out key name
144 var endIdx int
145 if len(keyQuote) > 0 {
146 startIdx := len(keyQuote)
147 // FIXME: fail case -> """"""name"""=value
148 pos := strings.Index(line[startIdx:], keyQuote)
149 if pos == -1 {
150 return "", -1, fmt.Errorf("missing closing key quote: %s", line)
151 }
152 pos += startIdx
153
154 // Find key-value delimiter
155 i := strings.IndexAny(line[pos+startIdx:], delimiters)
156 if i < 0 {
157 return "", -1, ErrDelimiterNotFound{line}
158 }
159 endIdx = pos + i
160 return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil
161 }
162
163 endIdx = strings.IndexAny(line, delimiters)
164 if endIdx < 0 {
165 return "", -1, ErrDelimiterNotFound{line}
166 }
167 if endIdx == 0 {
168 return "", -1, ErrEmptyKeyName{line}
169 }
170
171 return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil
172}
173
174func (p *parser) readMultilines(line, val, valQuote string) (string, error) {
175 for {
176 data, err := p.readUntil('\n')
177 if err != nil {
178 return "", err
179 }
180 next := string(data)
181
182 pos := strings.LastIndex(next, valQuote)
183 if pos > -1 {
184 val += next[:pos]
185
186 comment, has := cleanComment([]byte(next[pos:]))
187 if has {
188 p.comment.Write(bytes.TrimSpace(comment))
189 }
190 break
191 }
192 val += next
193 if p.isEOF {
194 return "", fmt.Errorf("missing closing key quote from %q to %q", line, next)
195 }
196 }
197 return val, nil
198}
199
200func (p *parser) readContinuationLines(val string) (string, error) {
201 for {
202 data, err := p.readUntil('\n')
203 if err != nil {
204 return "", err
205 }
206 next := strings.TrimSpace(string(data))
207
208 if len(next) == 0 {
209 break
210 }
211 val += next
212 if val[len(val)-1] != '\\' {
213 break
214 }
215 val = val[:len(val)-1]
216 }
217 return val, nil
218}
219
220// hasSurroundedQuote check if and only if the first and last characters
221// are quotes \" or \'.
222// It returns false if any other parts also contain same kind of quotes.
223func hasSurroundedQuote(in string, quote byte) bool {
224 return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote &&
225 strings.IndexByte(in[1:], quote) == len(in)-2
226}
227
228func (p *parser) readValue(in []byte, bufferSize int) (string, error) {
229
230 line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
231 if len(line) == 0 {
232 if p.options.AllowPythonMultilineValues && len(in) > 0 && in[len(in)-1] == '\n' {
233 return p.readPythonMultilines(line, bufferSize)
234 }
235 return "", nil
236 }
237
238 var valQuote string
239 if len(line) > 3 && line[0:3] == `"""` {
240 valQuote = `"""`
241 } else if line[0] == '`' {
242 valQuote = "`"
243 } else if p.options.UnescapeValueDoubleQuotes && line[0] == '"' {
244 valQuote = `"`
245 }
246
247 if len(valQuote) > 0 {
248 startIdx := len(valQuote)
249 pos := strings.LastIndex(line[startIdx:], valQuote)
250 // Check for multi-line value
251 if pos == -1 {
252 return p.readMultilines(line, line[startIdx:], valQuote)
253 }
254
255 if p.options.UnescapeValueDoubleQuotes && valQuote == `"` {
256 return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil
257 }
258 return line[startIdx : pos+startIdx], nil
259 }
260
261 lastChar := line[len(line)-1]
262 // Won't be able to reach here if value only contains whitespace
263 line = strings.TrimSpace(line)
264 trimmedLastChar := line[len(line)-1]
265
266 // Check continuation lines when desired
267 if !p.options.IgnoreContinuation && trimmedLastChar == '\\' {
268 return p.readContinuationLines(line[:len(line)-1])
269 }
270
271 // Check if ignore inline comment
272 if !p.options.IgnoreInlineComment {
273 var i int
274 if p.options.SpaceBeforeInlineComment {
275 i = strings.Index(line, " #")
276 if i == -1 {
277 i = strings.Index(line, " ;")
278 }
279
280 } else {
281 i = strings.IndexAny(line, "#;")
282 }
283
284 if i > -1 {
285 p.comment.WriteString(line[i:])
286 line = strings.TrimSpace(line[:i])
287 }
288
289 }
290
291 // Trim single and double quotes
292 if (hasSurroundedQuote(line, '\'') ||
293 hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote {
294 line = line[1 : len(line)-1]
295 } else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols {
296 line = strings.ReplaceAll(line, `\;`, ";")
297 line = strings.ReplaceAll(line, `\#`, "#")
298 } else if p.options.AllowPythonMultilineValues && lastChar == '\n' {
299 return p.readPythonMultilines(line, bufferSize)
300 }
301
302 return line, nil
303}
304
305func (p *parser) readPythonMultilines(line string, bufferSize int) (string, error) {
306 parserBufferPeekResult, _ := p.buf.Peek(bufferSize)
307 peekBuffer := bytes.NewBuffer(parserBufferPeekResult)
308
309 for {
310 peekData, peekErr := peekBuffer.ReadBytes('\n')
311 if peekErr != nil && peekErr != io.EOF {
312 p.debug("readPythonMultilines: failed to peek with error: %v", peekErr)
313 return "", peekErr
314 }
315
316 p.debug("readPythonMultilines: parsing %q", string(peekData))
317
318 peekMatches := pythonMultiline.FindStringSubmatch(string(peekData))
319 p.debug("readPythonMultilines: matched %d parts", len(peekMatches))
320 for n, v := range peekMatches {
321 p.debug(" %d: %q", n, v)
322 }
323
324 // Return if not a Python multiline value.
325 if len(peekMatches) != 3 {
326 p.debug("readPythonMultilines: end of value, got: %q", line)
327 return line, nil
328 }
329
330 // Advance the parser reader (buffer) in-sync with the peek buffer.
331 _, err := p.buf.Discard(len(peekData))
332 if err != nil {
333 p.debug("readPythonMultilines: failed to skip to the end, returning error")
334 return "", err
335 }
336
337 line += "\n" + peekMatches[0]
338 }
339}
340
341// parse parses data through an io.Reader.
342func (f *File) parse(reader io.Reader) (err error) {
343 p := newParser(reader, parserOptions{
344 IgnoreContinuation: f.options.IgnoreContinuation,
345 IgnoreInlineComment: f.options.IgnoreInlineComment,
346 AllowPythonMultilineValues: f.options.AllowPythonMultilineValues,
347 SpaceBeforeInlineComment: f.options.SpaceBeforeInlineComment,
348 UnescapeValueDoubleQuotes: f.options.UnescapeValueDoubleQuotes,
349 UnescapeValueCommentSymbols: f.options.UnescapeValueCommentSymbols,
350 PreserveSurroundedQuote: f.options.PreserveSurroundedQuote,
351 DebugFunc: f.options.DebugFunc,
352 ReaderBufferSize: f.options.ReaderBufferSize,
353 })
354 if err = p.BOM(); err != nil {
355 return fmt.Errorf("BOM: %v", err)
356 }
357
358 // Ignore error because default section name is never empty string.
359 name := DefaultSection
360 if f.options.Insensitive || f.options.InsensitiveSections {
361 name = strings.ToLower(DefaultSection)
362 }
363 section, _ := f.NewSection(name)
364
365 // This "last" is not strictly equivalent to "previous one" if current key is not the first nested key
366 var isLastValueEmpty bool
367 var lastRegularKey *Key
368
369 var line []byte
370 var inUnparseableSection bool
371
372 // NOTE: Iterate and increase `currentPeekSize` until
373 // the size of the parser buffer is found.
374 // TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`.
375 parserBufferSize := 0
376 // NOTE: Peek 4kb at a time.
377 currentPeekSize := minReaderBufferSize
378
379 if f.options.AllowPythonMultilineValues {
380 for {
381 peekBytes, _ := p.buf.Peek(currentPeekSize)
382 peekBytesLength := len(peekBytes)
383
384 if parserBufferSize >= peekBytesLength {
385 break
386 }
387
388 currentPeekSize *= 2
389 parserBufferSize = peekBytesLength
390 }
391 }
392
393 for !p.isEOF {
394 line, err = p.readUntil('\n')
395 if err != nil {
396 return err
397 }
398
399 if f.options.AllowNestedValues &&
400 isLastValueEmpty && len(line) > 0 {
401 if line[0] == ' ' || line[0] == '\t' {
402 err = lastRegularKey.addNestedValue(string(bytes.TrimSpace(line)))
403 if err != nil {
404 return err
405 }
406 continue
407 }
408 }
409
410 line = bytes.TrimLeftFunc(line, unicode.IsSpace)
411 if len(line) == 0 {
412 continue
413 }
414
415 // Comments
416 if line[0] == '#' || line[0] == ';' {
417 // Note: we do not care ending line break,
418 // it is needed for adding second line,
419 // so just clean it once at the end when set to value.
420 p.comment.Write(line)
421 continue
422 }
423
424 // Section
425 if line[0] == '[' {
426 // Read to the next ']' (TODO: support quoted strings)
427 closeIdx := bytes.LastIndexByte(line, ']')
428 if closeIdx == -1 {
429 return fmt.Errorf("unclosed section: %s", line)
430 }
431
432 name := string(line[1:closeIdx])
433 section, err = f.NewSection(name)
434 if err != nil {
435 return err
436 }
437
438 comment, has := cleanComment(line[closeIdx+1:])
439 if has {
440 p.comment.Write(comment)
441 }
442
443 section.Comment = strings.TrimSpace(p.comment.String())
444
445 // Reset auto-counter and comments
446 p.comment.Reset()
447 p.count = 1
448 // Nested values can't span sections
449 isLastValueEmpty = false
450
451 inUnparseableSection = false
452 for i := range f.options.UnparseableSections {
453 if f.options.UnparseableSections[i] == name ||
454 ((f.options.Insensitive || f.options.InsensitiveSections) && strings.EqualFold(f.options.UnparseableSections[i], name)) {
455 inUnparseableSection = true
456 continue
457 }
458 }
459 continue
460 }
461
462 if inUnparseableSection {
463 section.isRawSection = true
464 section.rawBody += string(line)
465 continue
466 }
467
468 kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line)
469 if err != nil {
470 switch {
471 // Treat as boolean key when desired, and whole line is key name.
472 case IsErrDelimiterNotFound(err):
473 switch {
474 case f.options.AllowBooleanKeys:
475 kname, err := p.readValue(line, parserBufferSize)
476 if err != nil {
477 return err
478 }
479 key, err := section.NewBooleanKey(kname)
480 if err != nil {
481 return err
482 }
483 key.Comment = strings.TrimSpace(p.comment.String())
484 p.comment.Reset()
485 continue
486
487 case f.options.SkipUnrecognizableLines:
488 continue
489 }
490 case IsErrEmptyKeyName(err) && f.options.SkipUnrecognizableLines:
491 continue
492 }
493 return err
494 }
495
496 // Auto increment.
497 isAutoIncr := false
498 if kname == "-" {
499 isAutoIncr = true
500 kname = "#" + strconv.Itoa(p.count)
501 p.count++
502 }
503
504 value, err := p.readValue(line[offset:], parserBufferSize)
505 if err != nil {
506 return err
507 }
508 isLastValueEmpty = len(value) == 0
509
510 key, err := section.NewKey(kname, value)
511 if err != nil {
512 return err
513 }
514 key.isAutoIncrement = isAutoIncr
515 key.Comment = strings.TrimSpace(p.comment.String())
516 p.comment.Reset()
517 lastRegularKey = key
518 }
519 return nil
520}
diff --git a/vendor/gopkg.in/ini.v1/section.go b/vendor/gopkg.in/ini.v1/section.go
deleted file mode 100644
index a3615d8..0000000
--- a/vendor/gopkg.in/ini.v1/section.go
+++ /dev/null
@@ -1,256 +0,0 @@
1// Copyright 2014 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17import (
18 "errors"
19 "fmt"
20 "strings"
21)
22
23// Section represents a config section.
24type Section struct {
25 f *File
26 Comment string
27 name string
28 keys map[string]*Key
29 keyList []string
30 keysHash map[string]string
31
32 isRawSection bool
33 rawBody string
34}
35
36func newSection(f *File, name string) *Section {
37 return &Section{
38 f: f,
39 name: name,
40 keys: make(map[string]*Key),
41 keyList: make([]string, 0, 10),
42 keysHash: make(map[string]string),
43 }
44}
45
46// Name returns name of Section.
47func (s *Section) Name() string {
48 return s.name
49}
50
51// Body returns rawBody of Section if the section was marked as unparseable.
52// It still follows the other rules of the INI format surrounding leading/trailing whitespace.
53func (s *Section) Body() string {
54 return strings.TrimSpace(s.rawBody)
55}
56
57// SetBody updates body content only if section is raw.
58func (s *Section) SetBody(body string) {
59 if !s.isRawSection {
60 return
61 }
62 s.rawBody = body
63}
64
65// NewKey creates a new key to given section.
66func (s *Section) NewKey(name, val string) (*Key, error) {
67 if len(name) == 0 {
68 return nil, errors.New("error creating new key: empty key name")
69 } else if s.f.options.Insensitive || s.f.options.InsensitiveKeys {
70 name = strings.ToLower(name)
71 }
72
73 if s.f.BlockMode {
74 s.f.lock.Lock()
75 defer s.f.lock.Unlock()
76 }
77
78 if inSlice(name, s.keyList) {
79 if s.f.options.AllowShadows {
80 if err := s.keys[name].addShadow(val); err != nil {
81 return nil, err
82 }
83 } else {
84 s.keys[name].value = val
85 s.keysHash[name] = val
86 }
87 return s.keys[name], nil
88 }
89
90 s.keyList = append(s.keyList, name)
91 s.keys[name] = newKey(s, name, val)
92 s.keysHash[name] = val
93 return s.keys[name], nil
94}
95
96// NewBooleanKey creates a new boolean type key to given section.
97func (s *Section) NewBooleanKey(name string) (*Key, error) {
98 key, err := s.NewKey(name, "true")
99 if err != nil {
100 return nil, err
101 }
102
103 key.isBooleanType = true
104 return key, nil
105}
106
107// GetKey returns key in section by given name.
108func (s *Section) GetKey(name string) (*Key, error) {
109 if s.f.BlockMode {
110 s.f.lock.RLock()
111 }
112 if s.f.options.Insensitive || s.f.options.InsensitiveKeys {
113 name = strings.ToLower(name)
114 }
115 key := s.keys[name]
116 if s.f.BlockMode {
117 s.f.lock.RUnlock()
118 }
119
120 if key == nil {
121 // Check if it is a child-section.
122 sname := s.name
123 for {
124 if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 {
125 sname = sname[:i]
126 sec, err := s.f.GetSection(sname)
127 if err != nil {
128 continue
129 }
130 return sec.GetKey(name)
131 }
132 break
133 }
134 return nil, fmt.Errorf("error when getting key of section %q: key %q not exists", s.name, name)
135 }
136 return key, nil
137}
138
139// HasKey returns true if section contains a key with given name.
140func (s *Section) HasKey(name string) bool {
141 key, _ := s.GetKey(name)
142 return key != nil
143}
144
145// Deprecated: Use "HasKey" instead.
146func (s *Section) Haskey(name string) bool {
147 return s.HasKey(name)
148}
149
150// HasValue returns true if section contains given raw value.
151func (s *Section) HasValue(value string) bool {
152 if s.f.BlockMode {
153 s.f.lock.RLock()
154 defer s.f.lock.RUnlock()
155 }
156
157 for _, k := range s.keys {
158 if value == k.value {
159 return true
160 }
161 }
162 return false
163}
164
165// Key assumes named Key exists in section and returns a zero-value when not.
166func (s *Section) Key(name string) *Key {
167 key, err := s.GetKey(name)
168 if err != nil {
169 // It's OK here because the only possible error is empty key name,
170 // but if it's empty, this piece of code won't be executed.
171 key, _ = s.NewKey(name, "")
172 return key
173 }
174 return key
175}
176
177// Keys returns list of keys of section.
178func (s *Section) Keys() []*Key {
179 keys := make([]*Key, len(s.keyList))
180 for i := range s.keyList {
181 keys[i] = s.Key(s.keyList[i])
182 }
183 return keys
184}
185
186// ParentKeys returns list of keys of parent section.
187func (s *Section) ParentKeys() []*Key {
188 var parentKeys []*Key
189 sname := s.name
190 for {
191 if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 {
192 sname = sname[:i]
193 sec, err := s.f.GetSection(sname)
194 if err != nil {
195 continue
196 }
197 parentKeys = append(parentKeys, sec.Keys()...)
198 } else {
199 break
200 }
201
202 }
203 return parentKeys
204}
205
206// KeyStrings returns list of key names of section.
207func (s *Section) KeyStrings() []string {
208 list := make([]string, len(s.keyList))
209 copy(list, s.keyList)
210 return list
211}
212
213// KeysHash returns keys hash consisting of names and values.
214func (s *Section) KeysHash() map[string]string {
215 if s.f.BlockMode {
216 s.f.lock.RLock()
217 defer s.f.lock.RUnlock()
218 }
219
220 hash := make(map[string]string, len(s.keysHash))
221 for key, value := range s.keysHash {
222 hash[key] = value
223 }
224 return hash
225}
226
227// DeleteKey deletes a key from section.
228func (s *Section) DeleteKey(name string) {
229 if s.f.BlockMode {
230 s.f.lock.Lock()
231 defer s.f.lock.Unlock()
232 }
233
234 for i, k := range s.keyList {
235 if k == name {
236 s.keyList = append(s.keyList[:i], s.keyList[i+1:]...)
237 delete(s.keys, name)
238 delete(s.keysHash, name)
239 return
240 }
241 }
242}
243
244// ChildSections returns a list of child sections of current section.
245// For example, "[parent.child1]" and "[parent.child12]" are child sections
246// of section "[parent]".
247func (s *Section) ChildSections() []*Section {
248 prefix := s.name + s.f.options.ChildSectionDelimiter
249 children := make([]*Section, 0, 3)
250 for _, name := range s.f.sectionList {
251 if strings.HasPrefix(name, prefix) {
252 children = append(children, s.f.sections[name]...)
253 }
254 }
255 return children
256}
diff --git a/vendor/gopkg.in/ini.v1/struct.go b/vendor/gopkg.in/ini.v1/struct.go
deleted file mode 100644
index a486b2f..0000000
--- a/vendor/gopkg.in/ini.v1/struct.go
+++ /dev/null
@@ -1,747 +0,0 @@
1// Copyright 2014 Unknwon
2//
3// Licensed under the Apache License, Version 2.0 (the "License"): you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations
13// under the License.
14
15package ini
16
17import (
18 "bytes"
19 "errors"
20 "fmt"
21 "reflect"
22 "strings"
23 "time"
24 "unicode"
25)
26
27// NameMapper represents a ini tag name mapper.
28type NameMapper func(string) string
29
30// Built-in name getters.
31var (
32 // SnackCase converts to format SNACK_CASE.
33 SnackCase NameMapper = func(raw string) string {
34 newstr := make([]rune, 0, len(raw))
35 for i, chr := range raw {
36 if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
37 if i > 0 {
38 newstr = append(newstr, '_')
39 }
40 }
41 newstr = append(newstr, unicode.ToUpper(chr))
42 }
43 return string(newstr)
44 }
45 // TitleUnderscore converts to format title_underscore.
46 TitleUnderscore NameMapper = func(raw string) string {
47 newstr := make([]rune, 0, len(raw))
48 for i, chr := range raw {
49 if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
50 if i > 0 {
51 newstr = append(newstr, '_')
52 }
53 chr -= 'A' - 'a'
54 }
55 newstr = append(newstr, chr)
56 }
57 return string(newstr)
58 }
59)
60
61func (s *Section) parseFieldName(raw, actual string) string {
62 if len(actual) > 0 {
63 return actual
64 }
65 if s.f.NameMapper != nil {
66 return s.f.NameMapper(raw)
67 }
68 return raw
69}
70
71func parseDelim(actual string) string {
72 if len(actual) > 0 {
73 return actual
74 }
75 return ","
76}
77
78var reflectTime = reflect.TypeOf(time.Now()).Kind()
79
80// setSliceWithProperType sets proper values to slice based on its type.
81func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
82 var strs []string
83 if allowShadow {
84 strs = key.StringsWithShadows(delim)
85 } else {
86 strs = key.Strings(delim)
87 }
88
89 numVals := len(strs)
90 if numVals == 0 {
91 return nil
92 }
93
94 var vals interface{}
95 var err error
96
97 sliceOf := field.Type().Elem().Kind()
98 switch sliceOf {
99 case reflect.String:
100 vals = strs
101 case reflect.Int:
102 vals, err = key.parseInts(strs, true, false)
103 case reflect.Int64:
104 vals, err = key.parseInt64s(strs, true, false)
105 case reflect.Uint:
106 vals, err = key.parseUints(strs, true, false)
107 case reflect.Uint64:
108 vals, err = key.parseUint64s(strs, true, false)
109 case reflect.Float64:
110 vals, err = key.parseFloat64s(strs, true, false)
111 case reflect.Bool:
112 vals, err = key.parseBools(strs, true, false)
113 case reflectTime:
114 vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)
115 default:
116 return fmt.Errorf("unsupported type '[]%s'", sliceOf)
117 }
118 if err != nil && isStrict {
119 return err
120 }
121
122 slice := reflect.MakeSlice(field.Type(), numVals, numVals)
123 for i := 0; i < numVals; i++ {
124 switch sliceOf {
125 case reflect.String:
126 slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i]))
127 case reflect.Int:
128 slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i]))
129 case reflect.Int64:
130 slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i]))
131 case reflect.Uint:
132 slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i]))
133 case reflect.Uint64:
134 slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i]))
135 case reflect.Float64:
136 slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i]))
137 case reflect.Bool:
138 slice.Index(i).Set(reflect.ValueOf(vals.([]bool)[i]))
139 case reflectTime:
140 slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i]))
141 }
142 }
143 field.Set(slice)
144 return nil
145}
146
147func wrapStrictError(err error, isStrict bool) error {
148 if isStrict {
149 return err
150 }
151 return nil
152}
153
154// setWithProperType sets proper value to field based on its type,
155// but it does not return error for failing parsing,
156// because we want to use default value that is already assigned to struct.
157func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
158 vt := t
159 isPtr := t.Kind() == reflect.Ptr
160 if isPtr {
161 vt = t.Elem()
162 }
163 switch vt.Kind() {
164 case reflect.String:
165 stringVal := key.String()
166 if isPtr {
167 field.Set(reflect.ValueOf(&stringVal))
168 } else if len(stringVal) > 0 {
169 field.SetString(key.String())
170 }
171 case reflect.Bool:
172 boolVal, err := key.Bool()
173 if err != nil {
174 return wrapStrictError(err, isStrict)
175 }
176 if isPtr {
177 field.Set(reflect.ValueOf(&boolVal))
178 } else {
179 field.SetBool(boolVal)
180 }
181 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
182 // ParseDuration will not return err for `0`, so check the type name
183 if vt.Name() == "Duration" {
184 durationVal, err := key.Duration()
185 if err != nil {
186 if intVal, err := key.Int64(); err == nil {
187 field.SetInt(intVal)
188 return nil
189 }
190 return wrapStrictError(err, isStrict)
191 }
192 if isPtr {
193 field.Set(reflect.ValueOf(&durationVal))
194 } else if int64(durationVal) > 0 {
195 field.Set(reflect.ValueOf(durationVal))
196 }
197 return nil
198 }
199
200 intVal, err := key.Int64()
201 if err != nil {
202 return wrapStrictError(err, isStrict)
203 }
204 if isPtr {
205 pv := reflect.New(t.Elem())
206 pv.Elem().SetInt(intVal)
207 field.Set(pv)
208 } else {
209 field.SetInt(intVal)
210 }
211 // byte is an alias for uint8, so supporting uint8 breaks support for byte
212 case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
213 durationVal, err := key.Duration()
214 // Skip zero value
215 if err == nil && uint64(durationVal) > 0 {
216 if isPtr {
217 field.Set(reflect.ValueOf(&durationVal))
218 } else {
219 field.Set(reflect.ValueOf(durationVal))
220 }
221 return nil
222 }
223
224 uintVal, err := key.Uint64()
225 if err != nil {
226 return wrapStrictError(err, isStrict)
227 }
228 if isPtr {
229 pv := reflect.New(t.Elem())
230 pv.Elem().SetUint(uintVal)
231 field.Set(pv)
232 } else {
233 field.SetUint(uintVal)
234 }
235
236 case reflect.Float32, reflect.Float64:
237 floatVal, err := key.Float64()
238 if err != nil {
239 return wrapStrictError(err, isStrict)
240 }
241 if isPtr {
242 pv := reflect.New(t.Elem())
243 pv.Elem().SetFloat(floatVal)
244 field.Set(pv)
245 } else {
246 field.SetFloat(floatVal)
247 }
248 case reflectTime:
249 timeVal, err := key.Time()
250 if err != nil {
251 return wrapStrictError(err, isStrict)
252 }
253 if isPtr {
254 field.Set(reflect.ValueOf(&timeVal))
255 } else {
256 field.Set(reflect.ValueOf(timeVal))
257 }
258 case reflect.Slice:
259 return setSliceWithProperType(key, field, delim, allowShadow, isStrict)
260 default:
261 return fmt.Errorf("unsupported type %q", t)
262 }
263 return nil
264}
265
266func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool, extends bool) {
267 opts := strings.SplitN(tag, ",", 5)
268 rawName = opts[0]
269 for _, opt := range opts[1:] {
270 omitEmpty = omitEmpty || (opt == "omitempty")
271 allowShadow = allowShadow || (opt == "allowshadow")
272 allowNonUnique = allowNonUnique || (opt == "nonunique")
273 extends = extends || (opt == "extends")
274 }
275 return rawName, omitEmpty, allowShadow, allowNonUnique, extends
276}
277
278// mapToField maps the given value to the matching field of the given section.
279// The sectionIndex is the index (if non unique sections are enabled) to which the value should be added.
280func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error {
281 if val.Kind() == reflect.Ptr {
282 val = val.Elem()
283 }
284 typ := val.Type()
285
286 for i := 0; i < typ.NumField(); i++ {
287 field := val.Field(i)
288 tpField := typ.Field(i)
289
290 tag := tpField.Tag.Get("ini")
291 if tag == "-" {
292 continue
293 }
294
295 rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
296 fieldName := s.parseFieldName(tpField.Name, rawName)
297 if len(fieldName) == 0 || !field.CanSet() {
298 continue
299 }
300
301 isStruct := tpField.Type.Kind() == reflect.Struct
302 isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct
303 isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
304 if isAnonymousPtr {
305 field.Set(reflect.New(tpField.Type.Elem()))
306 }
307
308 if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) {
309 if isStructPtr && field.IsNil() {
310 field.Set(reflect.New(tpField.Type.Elem()))
311 }
312 fieldSection := s
313 if rawName != "" {
314 sectionName = s.name + s.f.options.ChildSectionDelimiter + rawName
315 if secs, err := s.f.SectionsByName(sectionName); err == nil && sectionIndex < len(secs) {
316 fieldSection = secs[sectionIndex]
317 }
318 }
319 if err := fieldSection.mapToField(field, isStrict, sectionIndex, sectionName); err != nil {
320 return fmt.Errorf("map to field %q: %v", fieldName, err)
321 }
322 } else if isAnonymousPtr || isStruct || isStructPtr {
323 if secs, err := s.f.SectionsByName(fieldName); err == nil {
324 if len(secs) <= sectionIndex {
325 return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName)
326 }
327 // Only set the field to non-nil struct value if we have a section for it.
328 // Otherwise, we end up with a non-nil struct ptr even though there is no data.
329 if isStructPtr && field.IsNil() {
330 field.Set(reflect.New(tpField.Type.Elem()))
331 }
332 if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex, fieldName); err != nil {
333 return fmt.Errorf("map to field %q: %v", fieldName, err)
334 }
335 continue
336 }
337 }
338
339 // Map non-unique sections
340 if allowNonUnique && tpField.Type.Kind() == reflect.Slice {
341 newField, err := s.mapToSlice(fieldName, field, isStrict)
342 if err != nil {
343 return fmt.Errorf("map to slice %q: %v", fieldName, err)
344 }
345
346 field.Set(newField)
347 continue
348 }
349
350 if key, err := s.GetKey(fieldName); err == nil {
351 delim := parseDelim(tpField.Tag.Get("delim"))
352 if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
353 return fmt.Errorf("set field %q: %v", fieldName, err)
354 }
355 }
356 }
357 return nil
358}
359
360// mapToSlice maps all sections with the same name and returns the new value.
361// The type of the Value must be a slice.
362func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (reflect.Value, error) {
363 secs, err := s.f.SectionsByName(secName)
364 if err != nil {
365 return reflect.Value{}, err
366 }
367
368 typ := val.Type().Elem()
369 for i, sec := range secs {
370 elem := reflect.New(typ)
371 if err = sec.mapToField(elem, isStrict, i, sec.name); err != nil {
372 return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err)
373 }
374
375 val = reflect.Append(val, elem.Elem())
376 }
377 return val, nil
378}
379
380// mapTo maps a section to object v.
381func (s *Section) mapTo(v interface{}, isStrict bool) error {
382 typ := reflect.TypeOf(v)
383 val := reflect.ValueOf(v)
384 if typ.Kind() == reflect.Ptr {
385 typ = typ.Elem()
386 val = val.Elem()
387 } else {
388 return errors.New("not a pointer to a struct")
389 }
390
391 if typ.Kind() == reflect.Slice {
392 newField, err := s.mapToSlice(s.name, val, isStrict)
393 if err != nil {
394 return err
395 }
396
397 val.Set(newField)
398 return nil
399 }
400
401 return s.mapToField(val, isStrict, 0, s.name)
402}
403
404// MapTo maps section to given struct.
405func (s *Section) MapTo(v interface{}) error {
406 return s.mapTo(v, false)
407}
408
409// StrictMapTo maps section to given struct in strict mode,
410// which returns all possible error including value parsing error.
411func (s *Section) StrictMapTo(v interface{}) error {
412 return s.mapTo(v, true)
413}
414
415// MapTo maps file to given struct.
416func (f *File) MapTo(v interface{}) error {
417 return f.Section("").MapTo(v)
418}
419
420// StrictMapTo maps file to given struct in strict mode,
421// which returns all possible error including value parsing error.
422func (f *File) StrictMapTo(v interface{}) error {
423 return f.Section("").StrictMapTo(v)
424}
425
426// MapToWithMapper maps data sources to given struct with name mapper.
427func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
428 cfg, err := Load(source, others...)
429 if err != nil {
430 return err
431 }
432 cfg.NameMapper = mapper
433 return cfg.MapTo(v)
434}
435
436// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode,
437// which returns all possible error including value parsing error.
438func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
439 cfg, err := Load(source, others...)
440 if err != nil {
441 return err
442 }
443 cfg.NameMapper = mapper
444 return cfg.StrictMapTo(v)
445}
446
447// MapTo maps data sources to given struct.
448func MapTo(v, source interface{}, others ...interface{}) error {
449 return MapToWithMapper(v, nil, source, others...)
450}
451
452// StrictMapTo maps data sources to given struct in strict mode,
453// which returns all possible error including value parsing error.
454func StrictMapTo(v, source interface{}, others ...interface{}) error {
455 return StrictMapToWithMapper(v, nil, source, others...)
456}
457
458// reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
459func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error {
460 slice := field.Slice(0, field.Len())
461 if field.Len() == 0 {
462 return nil
463 }
464 sliceOf := field.Type().Elem().Kind()
465
466 if allowShadow {
467 var keyWithShadows *Key
468 for i := 0; i < field.Len(); i++ {
469 var val string
470 switch sliceOf {
471 case reflect.String:
472 val = slice.Index(i).String()
473 case reflect.Int, reflect.Int64:
474 val = fmt.Sprint(slice.Index(i).Int())
475 case reflect.Uint, reflect.Uint64:
476 val = fmt.Sprint(slice.Index(i).Uint())
477 case reflect.Float64:
478 val = fmt.Sprint(slice.Index(i).Float())
479 case reflect.Bool:
480 val = fmt.Sprint(slice.Index(i).Bool())
481 case reflectTime:
482 val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339)
483 default:
484 return fmt.Errorf("unsupported type '[]%s'", sliceOf)
485 }
486
487 if i == 0 {
488 keyWithShadows = newKey(key.s, key.name, val)
489 } else {
490 _ = keyWithShadows.AddShadow(val)
491 }
492 }
493 *key = *keyWithShadows
494 return nil
495 }
496
497 var buf bytes.Buffer
498 for i := 0; i < field.Len(); i++ {
499 switch sliceOf {
500 case reflect.String:
501 buf.WriteString(slice.Index(i).String())
502 case reflect.Int, reflect.Int64:
503 buf.WriteString(fmt.Sprint(slice.Index(i).Int()))
504 case reflect.Uint, reflect.Uint64:
505 buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))
506 case reflect.Float64:
507 buf.WriteString(fmt.Sprint(slice.Index(i).Float()))
508 case reflect.Bool:
509 buf.WriteString(fmt.Sprint(slice.Index(i).Bool()))
510 case reflectTime:
511 buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))
512 default:
513 return fmt.Errorf("unsupported type '[]%s'", sliceOf)
514 }
515 buf.WriteString(delim)
516 }
517 key.SetValue(buf.String()[:buf.Len()-len(delim)])
518 return nil
519}
520
521// reflectWithProperType does the opposite thing as setWithProperType.
522func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error {
523 switch t.Kind() {
524 case reflect.String:
525 key.SetValue(field.String())
526 case reflect.Bool:
527 key.SetValue(fmt.Sprint(field.Bool()))
528 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
529 key.SetValue(fmt.Sprint(field.Int()))
530 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
531 key.SetValue(fmt.Sprint(field.Uint()))
532 case reflect.Float32, reflect.Float64:
533 key.SetValue(fmt.Sprint(field.Float()))
534 case reflectTime:
535 key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))
536 case reflect.Slice:
537 return reflectSliceWithProperType(key, field, delim, allowShadow)
538 case reflect.Ptr:
539 if !field.IsNil() {
540 return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow)
541 }
542 default:
543 return fmt.Errorf("unsupported type %q", t)
544 }
545 return nil
546}
547
548// CR: copied from encoding/json/encode.go with modifications of time.Time support.
549// TODO: add more test coverage.
550func isEmptyValue(v reflect.Value) bool {
551 switch v.Kind() {
552 case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
553 return v.Len() == 0
554 case reflect.Bool:
555 return !v.Bool()
556 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
557 return v.Int() == 0
558 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
559 return v.Uint() == 0
560 case reflect.Float32, reflect.Float64:
561 return v.Float() == 0
562 case reflect.Interface, reflect.Ptr:
563 return v.IsNil()
564 case reflectTime:
565 t, ok := v.Interface().(time.Time)
566 return ok && t.IsZero()
567 }
568 return false
569}
570
571// StructReflector is the interface implemented by struct types that can extract themselves into INI objects.
572type StructReflector interface {
573 ReflectINIStruct(*File) error
574}
575
576func (s *Section) reflectFrom(val reflect.Value) error {
577 if val.Kind() == reflect.Ptr {
578 val = val.Elem()
579 }
580 typ := val.Type()
581
582 for i := 0; i < typ.NumField(); i++ {
583 if !val.Field(i).CanInterface() {
584 continue
585 }
586
587 field := val.Field(i)
588 tpField := typ.Field(i)
589
590 tag := tpField.Tag.Get("ini")
591 if tag == "-" {
592 continue
593 }
594
595 rawName, omitEmpty, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
596 if omitEmpty && isEmptyValue(field) {
597 continue
598 }
599
600 if r, ok := field.Interface().(StructReflector); ok {
601 return r.ReflectINIStruct(s.f)
602 }
603
604 fieldName := s.parseFieldName(tpField.Name, rawName)
605 if len(fieldName) == 0 || !field.CanSet() {
606 continue
607 }
608
609 if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Ptr || tpField.Type.Kind() == reflect.Struct) {
610 if err := s.reflectFrom(field); err != nil {
611 return fmt.Errorf("reflect from field %q: %v", fieldName, err)
612 }
613 continue
614 }
615
616 if (tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct) ||
617 (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {
618 // Note: The only error here is section doesn't exist.
619 sec, err := s.f.GetSection(fieldName)
620 if err != nil {
621 // Note: fieldName can never be empty here, ignore error.
622 sec, _ = s.f.NewSection(fieldName)
623 }
624
625 // Add comment from comment tag
626 if len(sec.Comment) == 0 {
627 sec.Comment = tpField.Tag.Get("comment")
628 }
629
630 if err = sec.reflectFrom(field); err != nil {
631 return fmt.Errorf("reflect from field %q: %v", fieldName, err)
632 }
633 continue
634 }
635
636 if allowNonUnique && tpField.Type.Kind() == reflect.Slice {
637 slice := field.Slice(0, field.Len())
638 if field.Len() == 0 {
639 return nil
640 }
641 sliceOf := field.Type().Elem().Kind()
642
643 for i := 0; i < field.Len(); i++ {
644 if sliceOf != reflect.Struct && sliceOf != reflect.Ptr {
645 return fmt.Errorf("field %q is not a slice of pointer or struct", fieldName)
646 }
647
648 sec, err := s.f.NewSection(fieldName)
649 if err != nil {
650 return err
651 }
652
653 // Add comment from comment tag
654 if len(sec.Comment) == 0 {
655 sec.Comment = tpField.Tag.Get("comment")
656 }
657
658 if err := sec.reflectFrom(slice.Index(i)); err != nil {
659 return fmt.Errorf("reflect from field %q: %v", fieldName, err)
660 }
661 }
662 continue
663 }
664
665 // Note: Same reason as section.
666 key, err := s.GetKey(fieldName)
667 if err != nil {
668 key, _ = s.NewKey(fieldName, "")
669 }
670
671 // Add comment from comment tag
672 if len(key.Comment) == 0 {
673 key.Comment = tpField.Tag.Get("comment")
674 }
675
676 delim := parseDelim(tpField.Tag.Get("delim"))
677 if err = reflectWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil {
678 return fmt.Errorf("reflect field %q: %v", fieldName, err)
679 }
680
681 }
682 return nil
683}
684
685// ReflectFrom reflects section from given struct. It overwrites existing ones.
686func (s *Section) ReflectFrom(v interface{}) error {
687 typ := reflect.TypeOf(v)
688 val := reflect.ValueOf(v)
689
690 if s.name != DefaultSection && s.f.options.AllowNonUniqueSections &&
691 (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr) {
692 // Clear sections to make sure none exists before adding the new ones
693 s.f.DeleteSection(s.name)
694
695 if typ.Kind() == reflect.Ptr {
696 sec, err := s.f.NewSection(s.name)
697 if err != nil {
698 return err
699 }
700 return sec.reflectFrom(val.Elem())
701 }
702
703 slice := val.Slice(0, val.Len())
704 sliceOf := val.Type().Elem().Kind()
705 if sliceOf != reflect.Ptr {
706 return fmt.Errorf("not a slice of pointers")
707 }
708
709 for i := 0; i < slice.Len(); i++ {
710 sec, err := s.f.NewSection(s.name)
711 if err != nil {
712 return err
713 }
714
715 err = sec.reflectFrom(slice.Index(i))
716 if err != nil {
717 return fmt.Errorf("reflect from %dth field: %v", i, err)
718 }
719 }
720
721 return nil
722 }
723
724 if typ.Kind() == reflect.Ptr {
725 val = val.Elem()
726 } else {
727 return errors.New("not a pointer to a struct")
728 }
729
730 return s.reflectFrom(val)
731}
732
733// ReflectFrom reflects file from given struct.
734func (f *File) ReflectFrom(v interface{}) error {
735 return f.Section("").ReflectFrom(v)
736}
737
738// ReflectFromWithMapper reflects data sources from given struct with name mapper.
739func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error {
740 cfg.NameMapper = mapper
741 return cfg.ReflectFrom(v)
742}
743
744// ReflectFrom reflects data sources from given struct.
745func ReflectFrom(cfg *File, v interface{}) error {
746 return ReflectFromWithMapper(cfg, v, nil)
747}