diff options
Diffstat (limited to 'vendor/gopkg.in/ini.v1')
-rw-r--r-- | vendor/gopkg.in/ini.v1/.editorconfig | 12 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/.gitignore | 7 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/.golangci.yml | 27 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/LICENSE | 191 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/Makefile | 15 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/README.md | 43 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/codecov.yml | 16 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/data_source.go | 76 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/deprecated.go | 22 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/error.go | 49 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/file.go | 541 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/helper.go | 24 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/ini.go | 176 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/key.go | 837 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/parser.go | 520 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/section.go | 256 | ||||
-rw-r--r-- | vendor/gopkg.in/ini.v1/struct.go | 747 |
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 | |||
3 | root = true | ||
4 | |||
5 | [*] | ||
6 | charset = utf-8 | ||
7 | end_of_line = lf | ||
8 | insert_final_newline = true | ||
9 | trim_trailing_whitespace = true | ||
10 | |||
11 | [*_test.go] | ||
12 | trim_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 @@ | |||
1 | testdata/conf_out.ini | ||
2 | ini.sublime-project | ||
3 | ini.sublime-workspace | ||
4 | testdata/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 @@ | |||
1 | linters-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 | |||
10 | linters: | ||
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 @@ | |||
1 | Apache License | ||
2 | Version 2.0, January 2004 | ||
3 | http://www.apache.org/licenses/ | ||
4 | |||
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
6 | |||
7 | 1. Definitions. | ||
8 | |||
9 | "License" shall mean the terms and conditions for use, reproduction, and | ||
10 | distribution as defined by Sections 1 through 9 of this document. | ||
11 | |||
12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright | ||
13 | owner that is granting the License. | ||
14 | |||
15 | "Legal Entity" shall mean the union of the acting entity and all other entities | ||
16 | that control, are controlled by, or are under common control with that entity. | ||
17 | For the purposes of this definition, "control" means (i) the power, direct or | ||
18 | indirect, to cause the direction or management of such entity, whether by | ||
19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
20 | outstanding shares, or (iii) beneficial ownership of such entity. | ||
21 | |||
22 | "You" (or "Your") shall mean an individual or Legal Entity exercising | ||
23 | permissions granted by this License. | ||
24 | |||
25 | "Source" form shall mean the preferred form for making modifications, including | ||
26 | but not limited to software source code, documentation source, and configuration | ||
27 | files. | ||
28 | |||
29 | "Object" form shall mean any form resulting from mechanical transformation or | ||
30 | translation of a Source form, including but not limited to compiled object code, | ||
31 | generated documentation, and conversions to other media types. | ||
32 | |||
33 | "Work" shall mean the work of authorship, whether in Source or Object form, made | ||
34 | available under the License, as indicated by a copyright notice that is included | ||
35 | in 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 | ||
38 | is based on (or derived from) the Work and for which the editorial revisions, | ||
39 | annotations, elaborations, or other modifications represent, as a whole, an | ||
40 | original work of authorship. For the purposes of this License, Derivative Works | ||
41 | shall not include works that remain separable from, or merely link (or bind by | ||
42 | name) to the interfaces of, the Work and Derivative Works thereof. | ||
43 | |||
44 | "Contribution" shall mean any work of authorship, including the original version | ||
45 | of the Work and any modifications or additions to that Work or Derivative Works | ||
46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work | ||
47 | by the copyright owner or by an individual or Legal Entity authorized to submit | ||
48 | on behalf of the copyright owner. For the purposes of this definition, | ||
49 | "submitted" means any form of electronic, verbal, or written communication sent | ||
50 | to the Licensor or its representatives, including but not limited to | ||
51 | communication on electronic mailing lists, source code control systems, and | ||
52 | issue tracking systems that are managed by, or on behalf of, the Licensor for | ||
53 | the purpose of discussing and improving the Work, but excluding communication | ||
54 | that is conspicuously marked or otherwise designated in writing by the copyright | ||
55 | owner as "Not a Contribution." | ||
56 | |||
57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf | ||
58 | of whom a Contribution has been received by Licensor and subsequently | ||
59 | incorporated within the Work. | ||
60 | |||
61 | 2. Grant of Copyright License. | ||
62 | |||
63 | Subject to the terms and conditions of this License, each Contributor hereby | ||
64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||
65 | irrevocable copyright license to reproduce, prepare Derivative Works of, | ||
66 | publicly display, publicly perform, sublicense, and distribute the Work and such | ||
67 | Derivative Works in Source or Object form. | ||
68 | |||
69 | 3. Grant of Patent License. | ||
70 | |||
71 | Subject to the terms and conditions of this License, each Contributor hereby | ||
72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||
73 | irrevocable (except as stated in this section) patent license to make, have | ||
74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where | ||
75 | such license applies only to those patent claims licensable by such Contributor | ||
76 | that are necessarily infringed by their Contribution(s) alone or by combination | ||
77 | of their Contribution(s) with the Work to which such Contribution(s) was | ||
78 | submitted. If You institute patent litigation against any entity (including a | ||
79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a | ||
80 | Contribution incorporated within the Work constitutes direct or contributory | ||
81 | patent infringement, then any patent licenses granted to You under this License | ||
82 | for that Work shall terminate as of the date such litigation is filed. | ||
83 | |||
84 | 4. Redistribution. | ||
85 | |||
86 | You may reproduce and distribute copies of the Work or Derivative Works thereof | ||
87 | in any medium, with or without modifications, and in Source or Object form, | ||
88 | provided that You meet the following conditions: | ||
89 | |||
90 | You must give any other recipients of the Work or Derivative Works a copy of | ||
91 | this License; and | ||
92 | You must cause any modified files to carry prominent notices stating that You | ||
93 | changed the files; and | ||
94 | You must retain, in the Source form of any Derivative Works that You distribute, | ||
95 | all copyright, patent, trademark, and attribution notices from the Source form | ||
96 | of the Work, excluding those notices that do not pertain to any part of the | ||
97 | Derivative Works; and | ||
98 | If the Work includes a "NOTICE" text file as part of its distribution, then any | ||
99 | Derivative Works that You distribute must include a readable copy of the | ||
100 | attribution notices contained within such NOTICE file, excluding those notices | ||
101 | that do not pertain to any part of the Derivative Works, in at least one of the | ||
102 | following places: within a NOTICE text file distributed as part of the | ||
103 | Derivative Works; within the Source form or documentation, if provided along | ||
104 | with the Derivative Works; or, within a display generated by the Derivative | ||
105 | Works, if and wherever such third-party notices normally appear. The contents of | ||
106 | the NOTICE file are for informational purposes only and do not modify the | ||
107 | License. You may add Your own attribution notices within Derivative Works that | ||
108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, | ||
109 | provided that such additional attribution notices cannot be construed as | ||
110 | modifying the License. | ||
111 | You may add Your own copyright statement to Your modifications and may provide | ||
112 | additional or different license terms and conditions for use, reproduction, or | ||
113 | distribution of Your modifications, or for any such Derivative Works as a whole, | ||
114 | provided Your use, reproduction, and distribution of the Work otherwise complies | ||
115 | with the conditions stated in this License. | ||
116 | |||
117 | 5. Submission of Contributions. | ||
118 | |||
119 | Unless You explicitly state otherwise, any Contribution intentionally submitted | ||
120 | for inclusion in the Work by You to the Licensor shall be under the terms and | ||
121 | conditions of this License, without any additional terms or conditions. | ||
122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of | ||
123 | any separate license agreement you may have executed with Licensor regarding | ||
124 | such Contributions. | ||
125 | |||
126 | 6. Trademarks. | ||
127 | |||
128 | This License does not grant permission to use the trade names, trademarks, | ||
129 | service marks, or product names of the Licensor, except as required for | ||
130 | reasonable and customary use in describing the origin of the Work and | ||
131 | reproducing the content of the NOTICE file. | ||
132 | |||
133 | 7. Disclaimer of Warranty. | ||
134 | |||
135 | Unless required by applicable law or agreed to in writing, Licensor provides the | ||
136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, | ||
137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, | ||
138 | including, without limitation, any warranties or conditions of TITLE, | ||
139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are | ||
140 | solely responsible for determining the appropriateness of using or | ||
141 | redistributing the Work and assume any risks associated with Your exercise of | ||
142 | permissions under this License. | ||
143 | |||
144 | 8. Limitation of Liability. | ||
145 | |||
146 | In no event and under no legal theory, whether in tort (including negligence), | ||
147 | contract, or otherwise, unless required by applicable law (such as deliberate | ||
148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be | ||
149 | liable to You for damages, including any direct, indirect, special, incidental, | ||
150 | or consequential damages of any character arising as a result of this License or | ||
151 | out of the use or inability to use the Work (including but not limited to | ||
152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or | ||
153 | any and all other commercial damages or losses), even if such Contributor has | ||
154 | been advised of the possibility of such damages. | ||
155 | |||
156 | 9. Accepting Warranty or Additional Liability. | ||
157 | |||
158 | While redistributing the Work or Derivative Works thereof, You may choose to | ||
159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or | ||
160 | other liability obligations and/or rights consistent with this License. However, | ||
161 | in accepting such obligations, You may act only on Your own behalf and on Your | ||
162 | sole responsibility, not on behalf of any other Contributor, and only if You | ||
163 | agree to indemnify, defend, and hold each Contributor harmless for any liability | ||
164 | incurred by, or claims asserted against, such Contributor by reason of your | ||
165 | accepting any such warranty or additional liability. | ||
166 | |||
167 | END OF TERMS AND CONDITIONS | ||
168 | |||
169 | APPENDIX: How to apply the Apache License to your work | ||
170 | |||
171 | To apply the Apache License to your work, attach the following boilerplate | ||
172 | notice, with the fields enclosed by brackets "[]" replaced with your own | ||
173 | identifying information. (Don't include the brackets!) The text should be | ||
174 | enclosed in the appropriate comment syntax for the file format. We also | ||
175 | recommend that a file or class name and description of purpose be included on | ||
176 | the same "printed page" as the copyright notice for easier identification within | ||
177 | third-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 | |||
3 | build: vet bench | ||
4 | |||
5 | test: | ||
6 | go test -v -cover -race | ||
7 | |||
8 | bench: | ||
9 | go test -v -cover -test.bench=. -test.benchmem | ||
10 | |||
11 | vet: | ||
12 | go vet | ||
13 | |||
14 | coverage: | ||
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 | |||
10 | Package 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 | |||
27 | The minimum requirement of Go is **1.13**. | ||
28 | |||
29 | ```sh | ||
30 | $ go get gopkg.in/ini.v1 | ||
31 | ``` | ||
32 | |||
33 | Please 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 | |||
43 | This 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 @@ | |||
1 | coverage: | ||
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 | |||
13 | comment: | ||
14 | layout: 'diff' | ||
15 | |||
16 | github_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 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
18 | "bytes" | ||
19 | "fmt" | ||
20 | "io" | ||
21 | "io/ioutil" | ||
22 | "os" | ||
23 | ) | ||
24 | |||
25 | var ( | ||
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. | ||
32 | type dataSource interface { | ||
33 | ReadCloser() (io.ReadCloser, error) | ||
34 | } | ||
35 | |||
36 | // sourceFile represents an object that contains content on the local file system. | ||
37 | type sourceFile struct { | ||
38 | name string | ||
39 | } | ||
40 | |||
41 | func (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. | ||
46 | type sourceData struct { | ||
47 | data []byte | ||
48 | } | ||
49 | |||
50 | func (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. | ||
55 | type sourceReadCloser struct { | ||
56 | reader io.ReadCloser | ||
57 | } | ||
58 | |||
59 | func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) { | ||
60 | return s.reader, nil | ||
61 | } | ||
62 | |||
63 | func 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 | |||
15 | package ini | ||
16 | |||
17 | var ( | ||
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 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
18 | "fmt" | ||
19 | ) | ||
20 | |||
21 | // ErrDelimiterNotFound indicates the error type of no delimiter is found which there should be one. | ||
22 | type ErrDelimiterNotFound struct { | ||
23 | Line string | ||
24 | } | ||
25 | |||
26 | // IsErrDelimiterNotFound returns true if the given error is an instance of ErrDelimiterNotFound. | ||
27 | func IsErrDelimiterNotFound(err error) bool { | ||
28 | _, ok := err.(ErrDelimiterNotFound) | ||
29 | return ok | ||
30 | } | ||
31 | |||
32 | func (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. | ||
37 | type ErrEmptyKeyName struct { | ||
38 | Line string | ||
39 | } | ||
40 | |||
41 | // IsErrEmptyKeyName returns true if the given error is an instance of ErrEmptyKeyName. | ||
42 | func IsErrEmptyKeyName(err error) bool { | ||
43 | _, ok := err.(ErrEmptyKeyName) | ||
44 | return ok | ||
45 | } | ||
46 | |||
47 | func (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 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
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. | ||
29 | type 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. | ||
51 | func 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. | ||
71 | func 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. | ||
83 | func (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. | ||
114 | func (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. | ||
126 | func (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. | ||
136 | func (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. | ||
146 | func (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. | ||
152 | func (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. | ||
174 | func (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. | ||
187 | func (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. | ||
200 | func (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. | ||
214 | func (f *File) ChildSections(name string) []*Section { | ||
215 | return f.Section(name).ChildSections() | ||
216 | } | ||
217 | |||
218 | // SectionStrings returns list of section names. | ||
219 | func (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. | ||
226 | func (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. | ||
241 | func (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 | |||
291 | func (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. | ||
302 | func (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. | ||
320 | func (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 | |||
336 | func (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. | ||
513 | func (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. | ||
522 | func (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. | ||
527 | func (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. | ||
539 | func (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 | |||
15 | package ini | ||
16 | |||
17 | func 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. | ||
16 | package ini | ||
17 | |||
18 | import ( | ||
19 | "os" | ||
20 | "regexp" | ||
21 | "runtime" | ||
22 | "strings" | ||
23 | ) | ||
24 | |||
25 | const ( | ||
26 | // Maximum allowed depth when recursively substituing variable names. | ||
27 | depthValues = 99 | ||
28 | ) | ||
29 | |||
30 | var ( | ||
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 | |||
58 | var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test") | ||
59 | |||
60 | func 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). | ||
67 | type 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. | ||
131 | type DebugFunc func(message string) | ||
132 | |||
133 | // LoadSources allows caller to apply customized options for loading from data source(s). | ||
134 | func 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. | ||
156 | func 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. | ||
162 | func 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. | ||
168 | func 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. | ||
174 | func 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 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
18 | "bytes" | ||
19 | "errors" | ||
20 | "fmt" | ||
21 | "strconv" | ||
22 | "strings" | ||
23 | "time" | ||
24 | ) | ||
25 | |||
26 | // Key represents a key under a section. | ||
27 | type 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. | ||
42 | func newKey(s *Section, name, val string) *Key { | ||
43 | return &Key{ | ||
44 | s: s, | ||
45 | name: name, | ||
46 | value: val, | ||
47 | } | ||
48 | } | ||
49 | |||
50 | func (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. | ||
76 | func (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 | |||
83 | func (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. | ||
93 | func (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 | ||
101 | type ValueMapper func(string) string | ||
102 | |||
103 | // Name returns name of key. | ||
104 | func (k *Key) Name() string { | ||
105 | return k.name | ||
106 | } | ||
107 | |||
108 | // Value returns raw value of key for performance purpose. | ||
109 | func (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. | ||
115 | func (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. | ||
137 | func (k *Key) NestedValues() []string { | ||
138 | return k.nestedValues | ||
139 | } | ||
140 | |||
141 | // transformValue takes a raw value and transforms to its final string. | ||
142 | func (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. | ||
179 | func (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. | ||
185 | func (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. | ||
194 | func 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. | ||
205 | func (k *Key) Bool() (bool, error) { | ||
206 | return parseBool(k.String()) | ||
207 | } | ||
208 | |||
209 | // Float64 returns float64 type value. | ||
210 | func (k *Key) Float64() (float64, error) { | ||
211 | return strconv.ParseFloat(k.String(), 64) | ||
212 | } | ||
213 | |||
214 | // Int returns int type value. | ||
215 | func (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. | ||
221 | func (k *Key) Int64() (int64, error) { | ||
222 | return strconv.ParseInt(k.String(), 0, 64) | ||
223 | } | ||
224 | |||
225 | // Uint returns uint type valued. | ||
226 | func (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. | ||
232 | func (k *Key) Uint64() (uint64, error) { | ||
233 | return strconv.ParseUint(k.String(), 0, 64) | ||
234 | } | ||
235 | |||
236 | // Duration returns time.Duration type value. | ||
237 | func (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. | ||
242 | func (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. | ||
247 | func (k *Key) Time() (time.Time, error) { | ||
248 | return k.TimeFormat(time.RFC3339) | ||
249 | } | ||
250 | |||
251 | // MustString returns default value if key value is empty. | ||
252 | func (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. | ||
263 | func (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. | ||
274 | func (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. | ||
285 | func (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. | ||
296 | func (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. | ||
307 | func (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. | ||
318 | func (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. | ||
329 | func (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. | ||
340 | func (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. | ||
351 | func (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. | ||
357 | func (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. | ||
369 | func (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. | ||
381 | func (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. | ||
393 | func (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. | ||
405 | func (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. | ||
417 | func (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. | ||
429 | func (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. | ||
441 | func (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. | ||
447 | func (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. | ||
457 | func (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. | ||
467 | func (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. | ||
477 | func (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. | ||
487 | func (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. | ||
492 | func (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. | ||
536 | func (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. | ||
554 | func (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. | ||
560 | func (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. | ||
566 | func (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. | ||
572 | func (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. | ||
578 | func (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. | ||
584 | func (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). | ||
591 | func (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). | ||
598 | func (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. | ||
604 | func (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. | ||
611 | func (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. | ||
618 | func (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. | ||
625 | func (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. | ||
632 | func (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. | ||
639 | func (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. | ||
645 | func (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. | ||
651 | func (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. | ||
656 | func (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. | ||
661 | func (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. | ||
666 | func (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. | ||
671 | func (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. | ||
676 | func (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. | ||
681 | func (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. | ||
687 | func (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. | ||
693 | func (k *Key) StrictTimes(delim string) ([]time.Time, error) { | ||
694 | return k.StrictTimesFormat(time.RFC3339, delim) | ||
695 | } | ||
696 | |||
697 | // parseBools transforms strings to bools. | ||
698 | func (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. | ||
714 | func (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. | ||
730 | func (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. | ||
746 | func (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. | ||
763 | func (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. | ||
780 | func (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 | |||
795 | type Parser func(str string) (interface{}, error) | ||
796 | |||
797 | // parseTimesFormat transforms strings to times in given format. | ||
798 | func (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 | ||
814 | func (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. | ||
829 | func (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 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
18 | "bufio" | ||
19 | "bytes" | ||
20 | "fmt" | ||
21 | "io" | ||
22 | "regexp" | ||
23 | "strconv" | ||
24 | "strings" | ||
25 | "unicode" | ||
26 | ) | ||
27 | |||
28 | const minReaderBufferSize = 4096 | ||
29 | |||
30 | var pythonMultiline = regexp.MustCompile(`^([\t\f ]+)(.*)`) | ||
31 | |||
32 | type 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 | |||
44 | type parser struct { | ||
45 | buf *bufio.Reader | ||
46 | options parserOptions | ||
47 | |||
48 | isEOF bool | ||
49 | count int | ||
50 | comment *bytes.Buffer | ||
51 | } | ||
52 | |||
53 | func (p *parser) debug(format string, args ...interface{}) { | ||
54 | if p.options.DebugFunc != nil { | ||
55 | p.options.DebugFunc(fmt.Sprintf(format, args...)) | ||
56 | } | ||
57 | } | ||
58 | |||
59 | func 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 | ||
75 | func (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 | |||
108 | func (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 | |||
120 | func 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 | |||
128 | func 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 | |||
174 | func (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 | |||
200 | func (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. | ||
223 | func 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 | |||
228 | func (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 | |||
305 | func (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. | ||
342 | func (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 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
18 | "errors" | ||
19 | "fmt" | ||
20 | "strings" | ||
21 | ) | ||
22 | |||
23 | // Section represents a config section. | ||
24 | type 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 | |||
36 | func 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. | ||
47 | func (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. | ||
53 | func (s *Section) Body() string { | ||
54 | return strings.TrimSpace(s.rawBody) | ||
55 | } | ||
56 | |||
57 | // SetBody updates body content only if section is raw. | ||
58 | func (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. | ||
66 | func (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. | ||
97 | func (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. | ||
108 | func (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. | ||
140 | func (s *Section) HasKey(name string) bool { | ||
141 | key, _ := s.GetKey(name) | ||
142 | return key != nil | ||
143 | } | ||
144 | |||
145 | // Deprecated: Use "HasKey" instead. | ||
146 | func (s *Section) Haskey(name string) bool { | ||
147 | return s.HasKey(name) | ||
148 | } | ||
149 | |||
150 | // HasValue returns true if section contains given raw value. | ||
151 | func (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. | ||
166 | func (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. | ||
178 | func (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. | ||
187 | func (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. | ||
207 | func (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. | ||
214 | func (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. | ||
228 | func (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]". | ||
247 | func (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 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
18 | "bytes" | ||
19 | "errors" | ||
20 | "fmt" | ||
21 | "reflect" | ||
22 | "strings" | ||
23 | "time" | ||
24 | "unicode" | ||
25 | ) | ||
26 | |||
27 | // NameMapper represents a ini tag name mapper. | ||
28 | type NameMapper func(string) string | ||
29 | |||
30 | // Built-in name getters. | ||
31 | var ( | ||
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 | |||
61 | func (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 | |||
71 | func parseDelim(actual string) string { | ||
72 | if len(actual) > 0 { | ||
73 | return actual | ||
74 | } | ||
75 | return "," | ||
76 | } | ||
77 | |||
78 | var reflectTime = reflect.TypeOf(time.Now()).Kind() | ||
79 | |||
80 | // setSliceWithProperType sets proper values to slice based on its type. | ||
81 | func 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 | |||
147 | func 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. | ||
157 | func 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 | |||
266 | func 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. | ||
280 | func (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. | ||
362 | func (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. | ||
381 | func (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. | ||
405 | func (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. | ||
411 | func (s *Section) StrictMapTo(v interface{}) error { | ||
412 | return s.mapTo(v, true) | ||
413 | } | ||
414 | |||
415 | // MapTo maps file to given struct. | ||
416 | func (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. | ||
422 | func (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. | ||
427 | func 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. | ||
438 | func 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. | ||
448 | func 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. | ||
454 | func StrictMapTo(v, source interface{}, others ...interface{}) error { | ||
455 | return StrictMapToWithMapper(v, nil, source, others...) | ||
456 | } | ||
457 | |||
458 | // reflectSliceWithProperType does the opposite thing as setSliceWithProperType. | ||
459 | func 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. | ||
522 | func 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. | ||
550 | func 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. | ||
572 | type StructReflector interface { | ||
573 | ReflectINIStruct(*File) error | ||
574 | } | ||
575 | |||
576 | func (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. | ||
686 | func (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. | ||
734 | func (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. | ||
739 | func 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. | ||
745 | func ReflectFrom(cfg *File, v interface{}) error { | ||
746 | return ReflectFromWithMapper(cfg, v, nil) | ||
747 | } | ||