go vendor
This commit is contained in:
19
vendor/github.com/revel/config/Doc/AUTHORS.md
generated
vendored
Normal file
19
vendor/github.com/revel/config/Doc/AUTHORS.md
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
###### Notice
|
||||
|
||||
*This is the official list of **config** authors for copyright purposes.*
|
||||
|
||||
*This file is distinct from the CONTRIBUTORS file. See the latter for an
|
||||
explanation.*
|
||||
|
||||
*Names should be added to this file as: `Organization`;
|
||||
`[Name](web address)` or `Name <email>` for individuals*
|
||||
|
||||
*Please keep the list sorted.*
|
||||
|
||||
* * *
|
||||
|
||||
[Jonas mg](https://github.com/kless)
|
||||
[Miguel Branco](https://github.com/msbranco)
|
||||
[Rob Figueiredo](https://github.com/robfig)
|
||||
[Tom Bruggeman](https://github.com/tmbrggmn)
|
||||
|
||||
28
vendor/github.com/revel/config/Doc/CONTRIBUTORS.md
generated
vendored
Normal file
28
vendor/github.com/revel/config/Doc/CONTRIBUTORS.md
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
###### Notice
|
||||
|
||||
*This is the official list of people who can contribute (and typically have
|
||||
contributed) code to the **config** repository.*
|
||||
|
||||
*The AUTHORS file lists the copyright holders; this file lists people. For
|
||||
example, the employees of an organization are listed here but not in AUTHORS,
|
||||
because the organization holds the copyright.*
|
||||
|
||||
*Names should be added to this file as: `[Name](web address)` or `Name <email>`*
|
||||
|
||||
*Please keep the list sorted.*
|
||||
|
||||
* * *
|
||||
|
||||
### Initial author
|
||||
|
||||
[Miguel Branco](https://github.com/msbranco)
|
||||
|
||||
### Maintainer
|
||||
|
||||
[Rob Figueiredo](https://github.com/robfig)
|
||||
|
||||
### Other authors
|
||||
|
||||
[Jonas mg](https://github.com/kless)
|
||||
[Tom Bruggeman](https://github.com/tmbrggmn)
|
||||
|
||||
202
vendor/github.com/revel/config/Doc/LICENSE_Apache.txt
generated
vendored
Normal file
202
vendor/github.com/revel/config/Doc/LICENSE_Apache.txt
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
52
vendor/github.com/revel/config/Doc/NEWS.md
generated
vendored
Normal file
52
vendor/github.com/revel/config/Doc/NEWS.md
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
###### Notice
|
||||
|
||||
*This file documents the changes in **config** versions that are listed below.*
|
||||
|
||||
*Items should be added to this file as:*
|
||||
|
||||
### YYYY-MM-DD Release
|
||||
|
||||
+ Additional changes.
|
||||
|
||||
+ More changes.
|
||||
|
||||
* * *
|
||||
|
||||
### 2011-??-?? v0.9.6
|
||||
|
||||
+ Changed to line comments.
|
||||
|
||||
|
||||
### 2010-09-15 v0.9.5
|
||||
|
||||
+ Sections, options and values are all case-sensitive.
|
||||
|
||||
+ Changed API:
|
||||
|
||||
Type *File* -> *Config*
|
||||
*NewFile()* -> *NewDefault*
|
||||
*ReadFile()* -> *ReadDefault*
|
||||
|
||||
+ Added functions, *New()*, *Read()*, which allow to choose the character of
|
||||
comment and separator, and the spaces around separator.
|
||||
|
||||
+ Better error handling.
|
||||
|
||||
+ Both sections and options are showed by its input order.
|
||||
|
||||
|
||||
### 2010-08-22 v0.9
|
||||
|
||||
+ The files has been splitted, formatted via *gomft*.
|
||||
|
||||
+ Methods use *self* to refer to its own type.
|
||||
|
||||
+ *Get* has been removed from the functions names.
|
||||
|
||||
+ Fixed some errors. All tests are passed.
|
||||
|
||||
+ At write the header in configuration file, it is added the comment character
|
||||
after of each new line.
|
||||
|
||||
+ Better documentation.
|
||||
|
||||
90
vendor/github.com/revel/config/README.md
generated
vendored
Normal file
90
vendor/github.com/revel/config/README.md
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
config
|
||||
======
|
||||
|
||||
This package implements a basic configuration file parser language which
|
||||
provides a structure similar to what you would find on Microsoft Windows INI
|
||||
files.
|
||||
|
||||
The configuration file consists of sections, led by a "*[section]*" header and
|
||||
followed by "*name: value*" entries; "*name=value*" is also accepted. Note that
|
||||
leading whitespace is removed from values. The optional values can contain
|
||||
format strings which refer to other values in the same section, or values in a
|
||||
special *DEFAULT* section. Additional defaults can be provided on initialization
|
||||
and retrieval. Comments are indicated by ";" or "#"; a comment may begin
|
||||
anywhere on a line, including on the same line after parameters or section
|
||||
declarations.
|
||||
|
||||
For example:
|
||||
|
||||
[My Section]
|
||||
foodir: %(dir)s/whatever
|
||||
dir=foo
|
||||
|
||||
would resolve the "*%(dir)s*" to the value of "*dir*" (*foo* in this case). All
|
||||
reference expansions are done on demand.
|
||||
|
||||
The functionality and workflow is loosely based on the *configparser* package of
|
||||
the Python Standard Library.
|
||||
|
||||
## Installation
|
||||
|
||||
go get github.com/revel/config
|
||||
|
||||
## Operating instructions
|
||||
|
||||
Given a sample configuration file:
|
||||
|
||||
[DEFAULT]
|
||||
host: www.example.com
|
||||
protocol: http://
|
||||
base-url: %(protocol)s%(host)s
|
||||
|
||||
[service-1]
|
||||
url: %(base-url)s/some/path
|
||||
delegation: on
|
||||
maxclients: 200 # do not set this higher
|
||||
comments: This is a multi-line
|
||||
entry # And this is a comment
|
||||
|
||||
To read this configuration file, do:
|
||||
|
||||
c, _ := config.ReadDefault("config.cfg")
|
||||
|
||||
c.String("service-1", "url")
|
||||
// result is string "http://www.example.com/some/path"
|
||||
|
||||
c.Int("service-1", "maxclients")
|
||||
// result is int 200
|
||||
|
||||
c.Bool("service-1", "delegation")
|
||||
// result is bool true
|
||||
|
||||
c.String("service-1", "comments")
|
||||
// result is string "This is a multi-line\nentry"
|
||||
|
||||
Note the support for unfolding variables (such as *%(base-url)s*), which are read
|
||||
from the special (reserved) section name *[DEFAULT]*.
|
||||
|
||||
A new configuration file can also be created with:
|
||||
|
||||
c := config.NewDefault()
|
||||
c.AddSection("Section")
|
||||
c.AddOption("Section", "option", "value")
|
||||
c.WriteFile("config.cfg", 0644, "A header for this file")
|
||||
|
||||
This results in the file:
|
||||
|
||||
# A header for this file
|
||||
|
||||
[Section]
|
||||
option: value
|
||||
|
||||
Note that sections, options and values are all case-sensitive.
|
||||
|
||||
## License
|
||||
|
||||
The source files are distributed under the [Mozilla Public License, version 2.0](http://mozilla.org/MPL/2.0/),
|
||||
unless otherwise noted.
|
||||
Please read the [FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html)
|
||||
if you have further questions regarding the license.
|
||||
|
||||
474
vendor/github.com/revel/config/all_test.go
generated
vendored
Normal file
474
vendor/github.com/revel/config/all_test.go
generated
vendored
Normal file
@@ -0,0 +1,474 @@
|
||||
// Copyright 2009 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
tmpFilename = "testdata/__test.go"
|
||||
sourceFilename = "testdata/source.cfg"
|
||||
targetFilename = "testdata/target.cfg"
|
||||
)
|
||||
|
||||
func testGet(t *testing.T, c *Config, section string, option string,
|
||||
expected interface{}) {
|
||||
ok := false
|
||||
switch expected.(type) {
|
||||
case string:
|
||||
v, _ := c.String(section, option)
|
||||
if v == expected.(string) {
|
||||
ok = true
|
||||
}
|
||||
case int:
|
||||
v, _ := c.Int(section, option)
|
||||
if v == expected.(int) {
|
||||
ok = true
|
||||
}
|
||||
case bool:
|
||||
v, _ := c.Bool(section, option)
|
||||
if v == expected.(bool) {
|
||||
ok = true
|
||||
}
|
||||
default:
|
||||
t.Fatalf("Bad test case")
|
||||
}
|
||||
if !ok {
|
||||
v, _ := c.String(section, option)
|
||||
t.Errorf("Get failure: expected different value for %s %s (expected: [%#v] got: [%#v])", section, option, expected, v)
|
||||
}
|
||||
}
|
||||
|
||||
// TestInMemory creates configuration representation and run multiple tests in-memory.
|
||||
func TestInMemory(t *testing.T) {
|
||||
c := NewDefault()
|
||||
|
||||
// == Test empty structure
|
||||
|
||||
// should be empty
|
||||
if len(c.Sections()) != 1 {
|
||||
t.Errorf("Sections failure: invalid length")
|
||||
}
|
||||
|
||||
// test presence of missing section
|
||||
if c.HasSection("no-section") {
|
||||
t.Errorf("HasSection failure: invalid section")
|
||||
}
|
||||
|
||||
// get options for missing section
|
||||
_, err := c.Options("no-section")
|
||||
if err == nil {
|
||||
t.Errorf("Options failure: invalid section")
|
||||
}
|
||||
|
||||
// test presence of option for missing section
|
||||
if c.HasOption("no-section", "no-option") {
|
||||
t.Errorf("HasSection failure: invalid/section/option")
|
||||
}
|
||||
|
||||
// get value from missing section/option
|
||||
_, err = c.String("no-section", "no-option")
|
||||
if err == nil {
|
||||
t.Errorf("String failure: got value for missing section/option")
|
||||
}
|
||||
|
||||
// get value from missing section/option
|
||||
_, err = c.Int("no-section", "no-option")
|
||||
if err == nil {
|
||||
t.Errorf("Int failure: got value for missing section/option")
|
||||
}
|
||||
|
||||
// remove missing section
|
||||
if c.RemoveSection("no-section") {
|
||||
t.Errorf("RemoveSection failure: removed missing section")
|
||||
}
|
||||
|
||||
// remove missing section/option
|
||||
if c.RemoveOption("no-section", "no-option") {
|
||||
t.Errorf("RemoveOption failure: removed missing section/option")
|
||||
}
|
||||
|
||||
// == Fill up structure
|
||||
|
||||
// add section
|
||||
if !c.AddSection("section1") {
|
||||
t.Errorf("AddSection failure: false on first insert")
|
||||
}
|
||||
|
||||
// re-add same section
|
||||
if c.AddSection("section1") {
|
||||
t.Errorf("AddSection failure: true on second insert")
|
||||
}
|
||||
|
||||
// default section always exists
|
||||
if c.AddSection(DefaultSection) {
|
||||
t.Errorf("AddSection failure: true on default section insert")
|
||||
}
|
||||
|
||||
// add option/value
|
||||
if !c.AddOption("section1", "option1", "value1") {
|
||||
t.Errorf("AddOption failure: false on first insert")
|
||||
}
|
||||
testGet(t, c, "section1", "option1", "value1") // read it back
|
||||
|
||||
// overwrite value
|
||||
if c.AddOption("section1", "option1", "value2") {
|
||||
t.Errorf("AddOption failure: true on second insert")
|
||||
}
|
||||
testGet(t, c, "section1", "option1", "value2") // read it back again
|
||||
|
||||
// remove option/value
|
||||
if !c.RemoveOption("section1", "option1") {
|
||||
t.Errorf("RemoveOption failure: false on first remove")
|
||||
}
|
||||
|
||||
// remove again
|
||||
if c.RemoveOption("section1", "option1") {
|
||||
t.Errorf("RemoveOption failure: true on second remove")
|
||||
}
|
||||
|
||||
// read it back again
|
||||
_, err = c.String("section1", "option1")
|
||||
if err == nil {
|
||||
t.Errorf("String failure: got value for removed section/option")
|
||||
}
|
||||
|
||||
// remove existing section
|
||||
if !c.RemoveSection("section1") {
|
||||
t.Errorf("RemoveSection failure: false on first remove")
|
||||
}
|
||||
|
||||
// remove again
|
||||
if c.RemoveSection("section1") {
|
||||
t.Errorf("RemoveSection failure: true on second remove")
|
||||
}
|
||||
|
||||
// == Test types
|
||||
|
||||
// add section
|
||||
if !c.AddSection("section2") {
|
||||
t.Errorf("AddSection failure: false on first insert")
|
||||
}
|
||||
|
||||
// add number
|
||||
if !c.AddOption("section2", "test-number", "666") {
|
||||
t.Errorf("AddOption failure: false on first insert")
|
||||
}
|
||||
testGet(t, c, "section2", "test-number", 666) // read it back
|
||||
|
||||
// add 'yes' (bool)
|
||||
if !c.AddOption("section2", "test-yes", "yes") {
|
||||
t.Errorf("AddOption failure: false on first insert")
|
||||
}
|
||||
testGet(t, c, "section2", "test-yes", true) // read it back
|
||||
|
||||
// add 'false' (bool)
|
||||
if !c.AddOption("section2", "test-false", "false") {
|
||||
t.Errorf("AddOption failure: false on first insert")
|
||||
}
|
||||
testGet(t, c, "section2", "test-false", false) // read it back
|
||||
|
||||
// == Test cycle
|
||||
|
||||
c.AddOption(DefaultSection, "opt1", "%(opt2)s")
|
||||
c.AddOption(DefaultSection, "opt2", "%(opt1)s")
|
||||
|
||||
_, err = c.String(DefaultSection, "opt1")
|
||||
if err == nil {
|
||||
t.Errorf("String failure: no error for cycle")
|
||||
} else if !strings.Contains(err.Error(), "cycle") {
|
||||
t.Errorf("String failure: incorrect error for cycle")
|
||||
}
|
||||
}
|
||||
|
||||
// TestReadFile creates a 'tough' configuration file and test (read) parsing.
|
||||
func TestReadFile(t *testing.T) {
|
||||
file, err := os.Create(tmpFilename)
|
||||
if err != nil {
|
||||
t.Fatal("Test cannot run because cannot write temporary file: " + tmpFilename)
|
||||
}
|
||||
|
||||
err = os.Setenv("GO_CONFIGFILE_TEST_ENV_VAR", "configvalue12345")
|
||||
if err != nil {
|
||||
t.Fatalf("Test cannot run because cannot set environment variable GO_CONFIGFILE_TEST_ENV_VAR: %#v", err)
|
||||
}
|
||||
|
||||
buf := bufio.NewWriter(file)
|
||||
buf.WriteString("optionInDefaultSection=true\n")
|
||||
buf.WriteString("[section-1]\n")
|
||||
buf.WriteString("option1=value1 ; This is a comment\n")
|
||||
buf.WriteString("option2 : 2#Not a comment\t#Now this is a comment after a TAB\n")
|
||||
buf.WriteString(" # Let me put another comment\n")
|
||||
buf.WriteString("option3= line1\n line2: \n\tline3=v # Comment multiline with := in value\n")
|
||||
buf.WriteString("; Another comment\n")
|
||||
buf.WriteString("[" + DefaultSection + "]\n")
|
||||
buf.WriteString("variable1=small\n")
|
||||
buf.WriteString("variable2=a_part_of_a_%(variable1)s_test\n")
|
||||
buf.WriteString("[secTION-2]\n")
|
||||
buf.WriteString("IS-flag-TRUE=Yes\n")
|
||||
buf.WriteString("[section-1] # comment on section header\n") // continue again [section-1]
|
||||
buf.WriteString("option4=this_is_%(variable2)s.\n")
|
||||
buf.WriteString("envoption1=this_uses_${GO_CONFIGFILE_TEST_ENV_VAR}_env\n")
|
||||
buf.WriteString("optionInDefaultSection=false")
|
||||
buf.Flush()
|
||||
file.Close()
|
||||
|
||||
c, err := ReadDefault(tmpFilename)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadDefault failure: %s", err)
|
||||
}
|
||||
|
||||
// check number of sections
|
||||
if len(c.Sections()) != 3 {
|
||||
t.Errorf("Sections failure: wrong number of sections")
|
||||
}
|
||||
|
||||
// check number of options 6 of [section-1] plus 2 of [default]
|
||||
opts, _ := c.Options("section-1")
|
||||
if len(opts) != 8 {
|
||||
t.Errorf("Options failure: wrong number of options: %d", len(opts))
|
||||
}
|
||||
|
||||
testGet(t, c, "section-1", "option1", "value1")
|
||||
testGet(t, c, "section-1", "option2", "2#Not a comment")
|
||||
testGet(t, c, "section-1", "option3", "line1\nline2:\nline3=v")
|
||||
testGet(t, c, "section-1", "option4", "this_is_a_part_of_a_small_test.")
|
||||
testGet(t, c, "section-1", "envoption1", "this_uses_configvalue12345_env")
|
||||
testGet(t, c, "section-1", "optionInDefaultSection", false)
|
||||
testGet(t, c, "section-2", "optionInDefaultSection", true)
|
||||
testGet(t, c, "secTION-2", "IS-flag-TRUE", true) // case-sensitive
|
||||
}
|
||||
|
||||
// TestWriteReadFile tests writing and reading back a configuration file.
|
||||
func TestWriteReadFile(t *testing.T) {
|
||||
cw := NewDefault()
|
||||
|
||||
// write file; will test only read later on
|
||||
cw.AddSection("First-Section")
|
||||
cw.AddOption("First-Section", "option1", "value option1")
|
||||
cw.AddOption("First-Section", "option2", "2")
|
||||
|
||||
cw.AddOption("", "host", "www.example.com")
|
||||
cw.AddOption(DefaultSection, "protocol", "https://")
|
||||
cw.AddOption(DefaultSection, "base-url", "%(protocol)s%(host)s")
|
||||
|
||||
cw.AddOption("Another-Section", "useHTTPS", "y")
|
||||
cw.AddOption("Another-Section", "url", "%(base-url)s/some/path")
|
||||
|
||||
cw.WriteFile(tmpFilename, 0644, "Test file for test-case")
|
||||
|
||||
// read back file and test
|
||||
cr, err := ReadDefault(tmpFilename)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadDefault failure: %s", err)
|
||||
}
|
||||
|
||||
testGet(t, cr, "First-Section", "option1", "value option1")
|
||||
testGet(t, cr, "First-Section", "option2", 2)
|
||||
testGet(t, cr, "Another-Section", "useHTTPS", true)
|
||||
testGet(t, cr, "Another-Section", "url", "https://www.example.com/some/path")
|
||||
|
||||
defer os.Remove(tmpFilename)
|
||||
}
|
||||
|
||||
// TestSectionOptions tests read options in a section without default options.
|
||||
func TestSectionOptions(t *testing.T) {
|
||||
cw := NewDefault()
|
||||
|
||||
// write file; will test only read later on
|
||||
cw.AddSection("First-Section")
|
||||
cw.AddOption("First-Section", "option1", "value option1")
|
||||
cw.AddOption("First-Section", "option2", "2")
|
||||
|
||||
cw.AddOption("", "host", "www.example.com")
|
||||
cw.AddOption(DefaultSection, "protocol", "https://")
|
||||
cw.AddOption(DefaultSection, "base-url", "%(protocol)s%(host)s")
|
||||
|
||||
cw.AddOption("Another-Section", "useHTTPS", "y")
|
||||
cw.AddOption("Another-Section", "url", "%(base-url)s/some/path")
|
||||
|
||||
cw.WriteFile(tmpFilename, 0644, "Test file for test-case")
|
||||
|
||||
// read back file and test
|
||||
cr, err := ReadDefault(tmpFilename)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadDefault failure: %s", err)
|
||||
}
|
||||
|
||||
options, err := cr.SectionOptions("First-Section")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("SectionOptions failure: %s", err)
|
||||
}
|
||||
|
||||
if len(options) != 2 {
|
||||
t.Fatalf("SectionOptions reads wrong data: %v", options)
|
||||
}
|
||||
|
||||
expected := map[string]bool{
|
||||
"option1": true,
|
||||
"option2": true,
|
||||
}
|
||||
actual := map[string]bool{}
|
||||
|
||||
for _, v := range options {
|
||||
actual[v] = true
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Fatalf("SectionOptions reads wrong data: %v", options)
|
||||
}
|
||||
|
||||
options, err = cr.SectionOptions(DefaultSection)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("SectionOptions failure: %s", err)
|
||||
}
|
||||
|
||||
expected = map[string]bool{
|
||||
"host": true,
|
||||
"protocol": true,
|
||||
"base-url": true,
|
||||
}
|
||||
actual = map[string]bool{}
|
||||
|
||||
for _, v := range options {
|
||||
actual[v] = true
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Fatalf("SectionOptions reads wrong data: %v", options)
|
||||
}
|
||||
|
||||
defer os.Remove(tmpFilename)
|
||||
}
|
||||
|
||||
// TestMerge tests merging 2 configurations.
|
||||
func TestMerge(t *testing.T) {
|
||||
target, error := ReadDefault(targetFilename)
|
||||
if error != nil {
|
||||
t.Fatalf("Unable to read target config file '%s'", targetFilename)
|
||||
}
|
||||
|
||||
source, error := ReadDefault(sourceFilename)
|
||||
if error != nil {
|
||||
t.Fatalf("Unable to read source config file '%s'", sourceFilename)
|
||||
}
|
||||
|
||||
target.Merge(source)
|
||||
|
||||
// Assert whether a regular option was merged from source -> target
|
||||
if result, _ := target.String(DefaultSection, "one"); result != "source1" {
|
||||
t.Errorf("Expected 'one' to be '1' but instead it was '%s'", result)
|
||||
}
|
||||
// Assert that a non-existent option in source was not overwritten
|
||||
if result, _ := target.String(DefaultSection, "five"); result != "5" {
|
||||
t.Errorf("Expected 'five' to be '5' but instead it was '%s'", result)
|
||||
}
|
||||
// Assert that a folded option was correctly unfolded
|
||||
if result, _ := target.String(DefaultSection, "two_+_three"); result != "source2 + source3" {
|
||||
t.Errorf("Expected 'two_+_three' to be 'source2 + source3' but instead it was '%s'", result)
|
||||
}
|
||||
if result, _ := target.String(DefaultSection, "four"); result != "4" {
|
||||
t.Errorf("Expected 'four' to be '4' but instead it was '%s'", result)
|
||||
}
|
||||
|
||||
// Assert that a section option has been merged
|
||||
if result, _ := target.String("X", "x.one"); result != "sourcex1" {
|
||||
t.Errorf("Expected '[X] x.one' to be 'sourcex1' but instead it was '%s'", result)
|
||||
}
|
||||
if result, _ := target.String("X", "x.four"); result != "x4" {
|
||||
t.Errorf("Expected '[X] x.four' to be 'x4' but instead it was '%s'", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadContextOneConf(t *testing.T) {
|
||||
ctx, err := LoadContext("app.conf", []string{"testdata/conf-path1"})
|
||||
if err != nil {
|
||||
t.Errorf("Error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
ctx.SetSection("X")
|
||||
result, found := ctx.String("x.three")
|
||||
if !strings.EqualFold("conf1-sourcex3", result) {
|
||||
t.Errorf("Expected '[X] x.three' to be 'conf1-sourcex3' but instead it was '%s'", result)
|
||||
}
|
||||
|
||||
_, found = ctx.String("x.notexists")
|
||||
if found {
|
||||
t.Error("Config 'x.notexists' shouldn't found")
|
||||
}
|
||||
|
||||
ctx.SetSection("Y")
|
||||
result, found = ctx.String("y.one")
|
||||
if !strings.EqualFold("conf1-sourcey1", result) {
|
||||
t.Errorf("Expected '[Y] y.one' to be 'conf1-sourcey1' but instead it was '%s'", result)
|
||||
}
|
||||
|
||||
_, found = ctx.String("y.notexists")
|
||||
if found {
|
||||
t.Error("Config 'y.notexists' shouldn't found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadContextMultipleConfWithPriority(t *testing.T) {
|
||||
ctx, err := LoadContext("app.conf", []string{"testdata/conf-path1", "testdata/conf-path2"})
|
||||
if err != nil {
|
||||
t.Errorf("Error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
ctx.SetSection("X")
|
||||
result, found := ctx.String("x.two")
|
||||
if !strings.EqualFold("override-conf2-sourcex2", result) {
|
||||
t.Errorf("Expected '[X] x.two' to be 'override-conf2-sourcex2' but instead it was '%s'", result)
|
||||
}
|
||||
|
||||
_, found = ctx.String("x.notexists")
|
||||
if found {
|
||||
t.Error("Config 'x.notexists' shouldn't be found")
|
||||
}
|
||||
|
||||
ctx.SetSection("Y")
|
||||
result, found = ctx.String("y.three")
|
||||
if !strings.EqualFold("override-conf2-sourcey3", result) {
|
||||
t.Errorf("Expected '[Y] y.three' to be 'override-conf2-sourcey3' but instead it was '%s'", result)
|
||||
}
|
||||
|
||||
_, found = ctx.String("y.notexists")
|
||||
if found {
|
||||
t.Error("Config 'y.notexists' shouldn't be found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadContextConfNotFound(t *testing.T) {
|
||||
_, err := LoadContext("notfound.conf", []string{"testdata/conf-path1"})
|
||||
if err != nil && !strings.EqualFold("open testdata/conf-path1/notfound.conf: no such file or directory", err.Error()) {
|
||||
t.Errorf("This is not expected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadContextInvalidConf(t *testing.T) {
|
||||
_, err := LoadContext("app-invalid.conf", []string{"testdata"})
|
||||
if err != nil && !strings.EqualFold("testdata/app-invalid.conf: could not parse line #7: %(two)s + %(four)s", err.Error()) {
|
||||
t.Errorf("This is not expected error: %v", err)
|
||||
}
|
||||
}
|
||||
152
vendor/github.com/revel/config/config.go
generated
vendored
Normal file
152
vendor/github.com/revel/config/config.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2009 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// config constants
|
||||
const (
|
||||
// Default section name.
|
||||
DefaultSection = "DEFAULT"
|
||||
// Maximum allowed depth when recursively substituing variable names.
|
||||
DepthValues = 200
|
||||
|
||||
DefaultComment = "# "
|
||||
AlternativeComment = "; "
|
||||
DefaultSeparator = ":"
|
||||
AlternativeSeparator = "="
|
||||
)
|
||||
|
||||
var (
|
||||
// Strings accepted as boolean.
|
||||
boolString = map[string]bool{
|
||||
"t": true,
|
||||
"true": true,
|
||||
"y": true,
|
||||
"yes": true,
|
||||
"on": true,
|
||||
"1": true,
|
||||
"f": false,
|
||||
"false": false,
|
||||
"n": false,
|
||||
"no": false,
|
||||
"off": false,
|
||||
"0": false,
|
||||
}
|
||||
|
||||
varRegExp = regexp.MustCompile(`%\(([a-zA-Z0-9_.\-]+)\)s`) // %(variable)s
|
||||
envVarRegExp = regexp.MustCompile(`\${([a-zA-Z0-9_.\-]+)}`) // ${envvar}
|
||||
)
|
||||
|
||||
// Config is the representation of configuration settings.
|
||||
type Config struct {
|
||||
comment string
|
||||
separator string
|
||||
|
||||
// Sections order
|
||||
lastIDSection int // Last section identifier
|
||||
idSection map[string]int // Section : position
|
||||
|
||||
// The last option identifier used for each section.
|
||||
lastIDOption map[string]int // Section : last identifier
|
||||
|
||||
// Section -> option : value
|
||||
data map[string]map[string]*tValue
|
||||
}
|
||||
|
||||
// tValue holds the input position for a value.
|
||||
type tValue struct {
|
||||
position int // Option order
|
||||
v string // value
|
||||
}
|
||||
|
||||
// New creates an empty configuration representation.
|
||||
// This representation can be filled with AddSection and AddOption and then
|
||||
// saved to a file using WriteFile.
|
||||
//
|
||||
// == Arguments
|
||||
//
|
||||
// comment: has to be `DefaultComment` or `AlternativeComment`
|
||||
// separator: has to be `DefaultSeparator` or `AlternativeSeparator`
|
||||
// preSpace: indicate if is inserted a space before of the separator
|
||||
// postSpace: indicate if is added a space after of the separator
|
||||
func New(comment, separator string, preSpace, postSpace bool) *Config {
|
||||
if comment != DefaultComment && comment != AlternativeComment {
|
||||
panic("comment character not valid")
|
||||
}
|
||||
|
||||
if separator != DefaultSeparator && separator != AlternativeSeparator {
|
||||
panic("separator character not valid")
|
||||
}
|
||||
|
||||
// == Get spaces around separator
|
||||
if preSpace {
|
||||
separator = " " + separator
|
||||
}
|
||||
|
||||
if postSpace {
|
||||
separator += " "
|
||||
}
|
||||
//==
|
||||
|
||||
c := new(Config)
|
||||
|
||||
c.comment = comment
|
||||
c.separator = separator
|
||||
c.idSection = make(map[string]int)
|
||||
c.lastIDOption = make(map[string]int)
|
||||
c.data = make(map[string]map[string]*tValue)
|
||||
|
||||
c.AddSection(DefaultSection) // Default section always exists.
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// NewDefault creates a configuration representation with values by default.
|
||||
func NewDefault() *Config {
|
||||
return New(DefaultComment, DefaultSeparator, false, true)
|
||||
}
|
||||
|
||||
// Merge merges the given configuration "source" with this one ("target").
|
||||
//
|
||||
// Merging means that any option (under any section) from source that is not in
|
||||
// target will be copied into target. When the target already has an option with
|
||||
// the same name and section then it is overwritten (i.o.w. the source wins).
|
||||
func (target *Config) Merge(source *Config) {
|
||||
if source == nil || source.data == nil || len(source.data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for section, option := range source.data {
|
||||
for optionName, optionValue := range option {
|
||||
target.AddOption(section, optionName, optionValue.v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// == Utility
|
||||
|
||||
func stripComments(l string) string {
|
||||
// Comments are preceded by space or TAB
|
||||
for _, c := range []string{" ;", "\t;", " #", "\t#"} {
|
||||
if i := strings.Index(l, c); i != -1 {
|
||||
l = l[0:i]
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
169
vendor/github.com/revel/config/context.go
generated
vendored
Normal file
169
vendor/github.com/revel/config/context.go
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright 2016 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Context structure handles the parsing of app.conf
|
||||
// It has a "preferred" section that is checked first for option queries.
|
||||
// If the preferred section does not have the option, the DEFAULT section is
|
||||
// checked fallback.
|
||||
type Context struct {
|
||||
config *Config
|
||||
section string // Check this section first, then fall back to DEFAULT
|
||||
}
|
||||
|
||||
// NewContext creates a default section and returns config context
|
||||
func NewContext() *Context {
|
||||
return &Context{config: NewDefault()}
|
||||
}
|
||||
|
||||
// LoadContext loads the ini config from gives multiple conf paths
|
||||
func LoadContext(confName string, confPaths []string) (*Context, error) {
|
||||
ctx := NewContext()
|
||||
for _, confPath := range confPaths {
|
||||
path := filepath.Join(confPath, confName)
|
||||
conf, err := ReadDefault(path)
|
||||
if err != nil {
|
||||
if _, isPathErr := err.(*os.PathError); !isPathErr {
|
||||
return nil, fmt.Errorf("%v: %v", path, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
ctx.config.Merge(conf)
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Raw returns raw config instance
|
||||
func (c *Context) Raw() *Config {
|
||||
return c.config
|
||||
}
|
||||
|
||||
// SetSection the section scope of ini config
|
||||
// For e.g.: dev or prod
|
||||
func (c *Context) SetSection(section string) {
|
||||
c.section = section
|
||||
}
|
||||
|
||||
// SetOption sets the value for the given key
|
||||
func (c *Context) SetOption(name, value string) {
|
||||
c.config.AddOption(c.section, name, value)
|
||||
}
|
||||
|
||||
// Int returns `int` config value and if found returns true
|
||||
// otherwise false
|
||||
func (c *Context) Int(option string) (result int, found bool) {
|
||||
result, err := c.config.Int(c.section, option)
|
||||
if err == nil {
|
||||
return result, true
|
||||
}
|
||||
if _, ok := err.(OptionError); ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// If it wasn't an OptionError, it must have failed to parse.
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// IntDefault returns `int` config value if found otherwise
|
||||
// returns given default int value
|
||||
func (c *Context) IntDefault(option string, dfault int) int {
|
||||
if r, found := c.Int(option); found {
|
||||
return r
|
||||
}
|
||||
return dfault
|
||||
}
|
||||
|
||||
// Bool returns `bool` config value and if found returns true
|
||||
// otherwise false
|
||||
func (c *Context) Bool(option string) (result, found bool) {
|
||||
result, err := c.config.Bool(c.section, option)
|
||||
if err == nil {
|
||||
return result, true
|
||||
}
|
||||
if _, ok := err.(OptionError); ok {
|
||||
return false, false
|
||||
}
|
||||
|
||||
// If it wasn't an OptionError, it must have failed to parse.
|
||||
return false, false
|
||||
}
|
||||
|
||||
// BoolDefault returns `bool` config value if found otherwise
|
||||
// returns given default bool value
|
||||
func (c *Context) BoolDefault(option string, dfault bool) bool {
|
||||
if r, found := c.Bool(option); found {
|
||||
return r
|
||||
}
|
||||
return dfault
|
||||
}
|
||||
|
||||
// String returns `string` config value and if found returns true
|
||||
// otherwise false
|
||||
func (c *Context) String(option string) (result string, found bool) {
|
||||
if r, err := c.config.String(c.section, option); err == nil {
|
||||
return stripQuotes(r), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// StringDefault returns `string` config value if found otherwise
|
||||
// returns given default string value
|
||||
func (c *Context) StringDefault(option, dfault string) string {
|
||||
if r, found := c.String(option); found {
|
||||
return r
|
||||
}
|
||||
return dfault
|
||||
}
|
||||
|
||||
// HasSection checks if the configuration has the given section.
|
||||
// (The default section always exists.)
|
||||
func (c *Context) HasSection(section string) bool {
|
||||
return c.config.HasSection(section)
|
||||
}
|
||||
|
||||
// Options returns all configuration option keys.
|
||||
// If a prefix is provided, then that is applied as a filter.
|
||||
func (c *Context) Options(prefix string) []string {
|
||||
var options []string
|
||||
keys, _ := c.config.Options(c.section)
|
||||
for _, key := range keys {
|
||||
if strings.HasPrefix(key, prefix) {
|
||||
options = append(options, key)
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
func stripQuotes(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
|
||||
if s[0] == '"' && s[len(s)-1] == '"' {
|
||||
return s[1 : len(s)-1]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
29
vendor/github.com/revel/config/error.go
generated
vendored
Normal file
29
vendor/github.com/revel/config/error.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2009 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
// SectionError type string
|
||||
type SectionError string
|
||||
|
||||
func (e SectionError) Error() string {
|
||||
return "section not found: " + string(e)
|
||||
}
|
||||
|
||||
// OptionError type string
|
||||
type OptionError string
|
||||
|
||||
func (e OptionError) Error() string {
|
||||
return "option not found: " + string(e)
|
||||
}
|
||||
113
vendor/github.com/revel/config/option.go
generated
vendored
Normal file
113
vendor/github.com/revel/config/option.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2009 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import "errors"
|
||||
|
||||
// AddOption adds a new option and value to the configuration.
|
||||
//
|
||||
// If the section is nil then uses the section by default; if it does not exist,
|
||||
// it is created in advance.
|
||||
//
|
||||
// It returns true if the option and value were inserted, and false if the value
|
||||
// was overwritten.
|
||||
func (c *Config) AddOption(section string, option string, value string) bool {
|
||||
c.AddSection(section) // Make sure section exists
|
||||
|
||||
if section == "" {
|
||||
section = DefaultSection
|
||||
}
|
||||
|
||||
_, ok := c.data[section][option]
|
||||
|
||||
c.data[section][option] = &tValue{c.lastIDOption[section], value}
|
||||
c.lastIDOption[section]++
|
||||
|
||||
return !ok
|
||||
}
|
||||
|
||||
// RemoveOption removes a option and value from the configuration.
|
||||
// It returns true if the option and value were removed, and false otherwise,
|
||||
// including if the section did not exist.
|
||||
func (c *Config) RemoveOption(section string, option string) bool {
|
||||
if _, ok := c.data[section]; !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := c.data[section][option]
|
||||
delete(c.data[section], option)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// HasOption checks if the configuration has the given option in the section.
|
||||
// It returns false if either the option or section do not exist.
|
||||
func (c *Config) HasOption(section string, option string) bool {
|
||||
if _, ok := c.data[section]; !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
_, okd := c.data[DefaultSection][option]
|
||||
_, oknd := c.data[section][option]
|
||||
|
||||
return okd || oknd
|
||||
}
|
||||
|
||||
// Options returns the list of options available in the given section.
|
||||
// It returns an error if the section does not exist and an empty list if the
|
||||
// section is empty. Options within the default section are also included.
|
||||
func (c *Config) Options(section string) (options []string, err error) {
|
||||
if _, ok := c.data[section]; !ok {
|
||||
return nil, errors.New(SectionError(section).Error())
|
||||
}
|
||||
|
||||
// Keep a map of option names we've seen to deduplicate.
|
||||
optionMap := make(map[string]struct{},
|
||||
len(c.data[DefaultSection])+len(c.data[section]))
|
||||
for s := range c.data[DefaultSection] {
|
||||
optionMap[s] = struct{}{}
|
||||
}
|
||||
for s := range c.data[section] {
|
||||
optionMap[s] = struct{}{}
|
||||
}
|
||||
|
||||
// Get the keys.
|
||||
i := 0
|
||||
options = make([]string, len(optionMap))
|
||||
for k := range optionMap {
|
||||
options[i] = k
|
||||
i++
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
// SectionOptions returns only the list of options available in the given section.
|
||||
// Unlike Options, SectionOptions doesn't return options in default section.
|
||||
// It returns an error if the section doesn't exist.
|
||||
func (c *Config) SectionOptions(section string) (options []string, err error) {
|
||||
if _, ok := c.data[section]; !ok {
|
||||
return nil, errors.New(SectionError(section).Error())
|
||||
}
|
||||
|
||||
options = make([]string, len(c.data[section]))
|
||||
i := 0
|
||||
for s := range c.data[section] {
|
||||
options[i] = s
|
||||
i++
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
102
vendor/github.com/revel/config/read.go
generated
vendored
Normal file
102
vendor/github.com/revel/config/read.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 2009 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// _read is the base to read a file and get the configuration representation.
|
||||
// That representation can be queried with GetString, etc.
|
||||
func _read(fname string, c *Config) (*Config, error) {
|
||||
file, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = c.read(bufio.NewReader(file)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Read reads a configuration file and returns its representation.
|
||||
// All arguments, except `fname`, are related to `New()`
|
||||
func Read(fname string, comment, separator string, preSpace, postSpace bool) (*Config, error) {
|
||||
return _read(fname, New(comment, separator, preSpace, postSpace))
|
||||
}
|
||||
|
||||
// ReadDefault reads a configuration file and returns its representation.
|
||||
// It uses values by default.
|
||||
func ReadDefault(fname string) (*Config, error) {
|
||||
return _read(fname, NewDefault())
|
||||
}
|
||||
|
||||
// * * *
|
||||
|
||||
func (c *Config) read(buf *bufio.Reader) (err error) {
|
||||
var section, option string
|
||||
var scanner = bufio.NewScanner(buf)
|
||||
lineNo := 0
|
||||
for scanner.Scan() {
|
||||
l := strings.TrimRightFunc(stripComments(scanner.Text()), unicode.IsSpace)
|
||||
lineNo++
|
||||
|
||||
// Switch written for readability (not performance)
|
||||
switch {
|
||||
// Empty line and comments
|
||||
case len(l) == 0, l[0] == '#', l[0] == ';':
|
||||
continue
|
||||
|
||||
// New section. The [ must be at the start of the line
|
||||
case l[0] == '[' && l[len(l)-1] == ']':
|
||||
option = "" // reset multi-line value
|
||||
section = strings.TrimSpace(l[1 : len(l)-1])
|
||||
c.AddSection(section)
|
||||
|
||||
// Continuation of multi-line value
|
||||
// starts with whitespace, we're in a section and working on an option
|
||||
case section != "" && option != "" && (l[0] == ' ' || l[0] == '\t'):
|
||||
prev, _ := c.RawString(section, option)
|
||||
value := strings.TrimSpace(l)
|
||||
c.AddOption(section, option, prev+"\n"+value)
|
||||
|
||||
// Other alternatives
|
||||
default:
|
||||
i := strings.IndexAny(l, "=:")
|
||||
|
||||
switch {
|
||||
// Option and value
|
||||
case i > 0 && l[0] != ' ' && l[0] != '\t': // found an =: and it's not a multiline continuation
|
||||
option = strings.TrimSpace(l[0:i])
|
||||
value := strings.TrimSpace(l[i+1:])
|
||||
c.AddOption(section, option, value)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("could not parse line #%v: %v", lineNo, l)
|
||||
}
|
||||
}
|
||||
}
|
||||
return scanner.Err()
|
||||
}
|
||||
88
vendor/github.com/revel/config/section.go
generated
vendored
Normal file
88
vendor/github.com/revel/config/section.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2009 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
// AddSection adds a new section to the configuration.
|
||||
//
|
||||
// If the section is nil then uses the section by default which it's already
|
||||
// created.
|
||||
//
|
||||
// It returns true if the new section was inserted, and false if the section
|
||||
// already existed.
|
||||
func (c *Config) AddSection(section string) bool {
|
||||
// DefaultSection
|
||||
if section == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, ok := c.data[section]; ok {
|
||||
return false
|
||||
}
|
||||
|
||||
c.data[section] = make(map[string]*tValue)
|
||||
|
||||
// Section order
|
||||
c.idSection[section] = c.lastIDSection
|
||||
c.lastIDSection++
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// RemoveSection removes a section from the configuration.
|
||||
// It returns true if the section was removed, and false if section did not exist.
|
||||
func (c *Config) RemoveSection(section string) bool {
|
||||
_, ok := c.data[section]
|
||||
|
||||
// Default section cannot be removed.
|
||||
if !ok || section == DefaultSection {
|
||||
return false
|
||||
}
|
||||
|
||||
for o := range c.data[section] {
|
||||
delete(c.data[section], o) // *value
|
||||
}
|
||||
delete(c.data, section)
|
||||
|
||||
delete(c.lastIDOption, section)
|
||||
delete(c.idSection, section)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// HasSection checks if the configuration has the given section.
|
||||
// (The default section always exists.)
|
||||
func (c *Config) HasSection(section string) bool {
|
||||
_, ok := c.data[section]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// Sections returns the list of sections in the configuration.
|
||||
// (The default section always exists).
|
||||
func (c *Config) Sections() (sections []string) {
|
||||
sections = make([]string, len(c.idSection))
|
||||
pos := 0 // Position in sections
|
||||
|
||||
for i := 0; i < c.lastIDSection; i++ {
|
||||
for section, id := range c.idSection {
|
||||
if id == i {
|
||||
sections[pos] = section
|
||||
pos++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sections
|
||||
}
|
||||
17
vendor/github.com/revel/config/testdata/app-invalid.conf
generated
vendored
Normal file
17
vendor/github.com/revel/config/testdata/app-invalid.conf
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
one=source1
|
||||
two=source2
|
||||
three=source3
|
||||
four=4
|
||||
|
||||
#invalid spot
|
||||
%(two)s + %(four)s
|
||||
|
||||
[X]
|
||||
x.one=conf1-sourcex1
|
||||
x.two=conf1-sourcex2
|
||||
x.three=conf1-sourcex3
|
||||
|
||||
[Y]
|
||||
y.one=conf1-sourcey1
|
||||
y.two=conf1-sourcey2
|
||||
y.three=conf1-sourcey3
|
||||
16
vendor/github.com/revel/config/testdata/conf-path1/app.conf
generated
vendored
Normal file
16
vendor/github.com/revel/config/testdata/conf-path1/app.conf
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
one=source1
|
||||
two=source2
|
||||
three=source3
|
||||
four=4
|
||||
|
||||
two_+_four=%(two)s + %(four)s
|
||||
|
||||
[X]
|
||||
x.one=conf1-sourcex1
|
||||
x.two=conf1-sourcex2
|
||||
x.three=conf1-sourcex3
|
||||
|
||||
[Y]
|
||||
y.one=conf1-sourcey1
|
||||
y.two=conf1-sourcey2
|
||||
y.three=conf1-sourcey3
|
||||
18
vendor/github.com/revel/config/testdata/conf-path2/app.conf
generated
vendored
Normal file
18
vendor/github.com/revel/config/testdata/conf-path2/app.conf
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
one=source1
|
||||
two=source2
|
||||
three=source3
|
||||
four=4
|
||||
|
||||
two_+_four=%(two)s + %(four)s
|
||||
|
||||
[X]
|
||||
x.one=conf2-sourcex1
|
||||
x.two=override-conf2-sourcex2
|
||||
x.three=conf2-sourcex3
|
||||
|
||||
x.four=exists here only
|
||||
|
||||
[Y]
|
||||
y.one=conf2-sourcey1
|
||||
y.two=conf2-sourcey2
|
||||
y.three=override-conf2-sourcey3
|
||||
17
vendor/github.com/revel/config/testdata/source.cfg
generated
vendored
Normal file
17
vendor/github.com/revel/config/testdata/source.cfg
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
one=source1
|
||||
two=source2
|
||||
three=source3
|
||||
four=4
|
||||
|
||||
two_+_four=%(two)s + %(four)s
|
||||
|
||||
[X]
|
||||
x.one=sourcex1
|
||||
x.two=sourcex2
|
||||
x.three=sourcex3
|
||||
|
||||
[Y]
|
||||
y.one=sourcey1
|
||||
y.two=sourcey2
|
||||
y.three=sourcey3
|
||||
|
||||
19
vendor/github.com/revel/config/testdata/target.cfg
generated
vendored
Normal file
19
vendor/github.com/revel/config/testdata/target.cfg
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
one=1
|
||||
two=2
|
||||
three=3
|
||||
five=5
|
||||
|
||||
two_+_three=%(two)s + %(three)s
|
||||
|
||||
[X]
|
||||
x.one=x1
|
||||
x.two=x2
|
||||
x.three=x3
|
||||
x.four=x4
|
||||
|
||||
[Y]
|
||||
y.one=y1
|
||||
y.two=y2
|
||||
y.three=y3
|
||||
y.four=y4
|
||||
|
||||
155
vendor/github.com/revel/config/type.go
generated
vendored
Normal file
155
vendor/github.com/revel/config/type.go
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2009 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Substitutes values, calculated by callback, on matching regex
|
||||
func (c *Config) computeVar(beforeValue *string, regx *regexp.Regexp, headsz, tailsz int, withVar func(*string) string) (*string, error) {
|
||||
var i int
|
||||
computedVal := beforeValue
|
||||
for i = 0; i < DepthValues; i++ { // keep a sane depth
|
||||
|
||||
vr := regx.FindStringSubmatchIndex(*computedVal)
|
||||
if len(vr) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
varname := (*computedVal)[vr[headsz]:vr[headsz+1]]
|
||||
varVal := withVar(&varname)
|
||||
if varVal == "" {
|
||||
return &varVal, fmt.Errorf("Option not found: %s", varname)
|
||||
}
|
||||
|
||||
// substitute by new value and take off leading '%(' and trailing ')s'
|
||||
// %(foo)s => headsz=2, tailsz=2
|
||||
// ${foo} => headsz=2, tailsz=1
|
||||
newVal := (*computedVal)[0:vr[headsz]-headsz] + varVal + (*computedVal)[vr[headsz+1]+tailsz:]
|
||||
computedVal = &newVal
|
||||
}
|
||||
|
||||
if i == DepthValues {
|
||||
retVal := ""
|
||||
return &retVal,
|
||||
fmt.Errorf("Possible cycle while unfolding variables: max depth of %d reached", DepthValues)
|
||||
}
|
||||
|
||||
return computedVal, nil
|
||||
}
|
||||
|
||||
// Bool has the same behaviour as String but converts the response to bool.
|
||||
// See "boolString" for string values converted to bool.
|
||||
func (c *Config) Bool(section string, option string) (value bool, err error) {
|
||||
sv, err := c.String(section, option)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
value, ok := boolString[strings.ToLower(sv)]
|
||||
if !ok {
|
||||
return false, errors.New("could not parse bool value: " + sv)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Float has the same behaviour as String but converts the response to float.
|
||||
func (c *Config) Float(section string, option string) (value float64, err error) {
|
||||
sv, err := c.String(section, option)
|
||||
if err == nil {
|
||||
value, err = strconv.ParseFloat(sv, 64)
|
||||
}
|
||||
|
||||
return value, err
|
||||
}
|
||||
|
||||
// Int has the same behaviour as String but converts the response to int.
|
||||
func (c *Config) Int(section string, option string) (value int, err error) {
|
||||
sv, err := c.String(section, option)
|
||||
if err == nil {
|
||||
value, err = strconv.Atoi(sv)
|
||||
}
|
||||
|
||||
return value, err
|
||||
}
|
||||
|
||||
// RawString gets the (raw) string value for the given option in the section.
|
||||
// The raw string value is not subjected to unfolding, which was illustrated in
|
||||
// the beginning of this documentation.
|
||||
//
|
||||
// It returns an error if either the section or the option do not exist.
|
||||
func (c *Config) RawString(section string, option string) (value string, err error) {
|
||||
if _, ok := c.data[section]; ok {
|
||||
if tValue, ok := c.data[section][option]; ok {
|
||||
return tValue.v, nil
|
||||
}
|
||||
}
|
||||
return c.RawStringDefault(option)
|
||||
}
|
||||
|
||||
// RawStringDefault gets the (raw) string value for the given option from the
|
||||
// DEFAULT section.
|
||||
//
|
||||
// It returns an error if the option does not exist in the DEFAULT section.
|
||||
func (c *Config) RawStringDefault(option string) (value string, err error) {
|
||||
if tValue, ok := c.data[DefaultSection][option]; ok {
|
||||
return tValue.v, nil
|
||||
}
|
||||
return "", OptionError(option)
|
||||
}
|
||||
|
||||
// String gets the string value for the given option in the section.
|
||||
// If the value needs to be unfolded (see e.g. %(host)s example in the beginning
|
||||
// of this documentation), then String does this unfolding automatically, up to
|
||||
// `DepthValues` number of iterations.
|
||||
//
|
||||
// It returns an error if either the section or the option do not exist, or the
|
||||
// unfolding cycled.
|
||||
func (c *Config) String(section string, option string) (value string, err error) {
|
||||
value, err = c.RawString(section, option)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// % variables
|
||||
computedVal, err := c.computeVar(&value, varRegExp, 2, 2, func(varName *string) string {
|
||||
lowerVar := *varName
|
||||
// search variable in default section as well as current section
|
||||
varVal, _ := c.data[DefaultSection][lowerVar]
|
||||
if _, ok := c.data[section][lowerVar]; ok {
|
||||
varVal = c.data[section][lowerVar]
|
||||
}
|
||||
return varVal.v
|
||||
})
|
||||
value = *computedVal
|
||||
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
|
||||
// $ environment variables
|
||||
computedVal, err = c.computeVar(&value, envVarRegExp, 2, 1, func(varName *string) string {
|
||||
return os.Getenv(*varName)
|
||||
})
|
||||
value = *computedVal
|
||||
return value, err
|
||||
}
|
||||
87
vendor/github.com/revel/config/write.go
generated
vendored
Normal file
87
vendor/github.com/revel/config/write.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2009 The "config" Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WriteFile saves the configuration representation to a file.
|
||||
// The desired file permissions must be passed as in os.Open. The header is a
|
||||
// string that is saved as a comment in the first line of the file.
|
||||
func (c *Config) WriteFile(fname string, perm os.FileMode, header string) error {
|
||||
file, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := bufio.NewWriter(file)
|
||||
if err = c.write(buf, header); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = buf.Flush()
|
||||
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
func (c *Config) write(buf *bufio.Writer, header string) (err error) {
|
||||
if header != "" {
|
||||
// Add comment character after of each new line.
|
||||
if i := strings.Index(header, "\n"); i != -1 {
|
||||
header = strings.Replace(header, "\n", "\n"+c.comment, -1)
|
||||
}
|
||||
|
||||
if _, err = buf.WriteString(c.comment + header + "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, orderedSection := range c.Sections() {
|
||||
for section, sectionMap := range c.data {
|
||||
if section == orderedSection {
|
||||
|
||||
// Skip default section if empty.
|
||||
if section == DefaultSection && len(sectionMap) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err = buf.WriteString("\n[" + section + "]\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Follow the input order in options.
|
||||
for i := 0; i < c.lastIDOption[section]; i++ {
|
||||
for option, tValue := range sectionMap {
|
||||
|
||||
if tValue.position == i {
|
||||
if _, err = buf.WriteString(fmt.Sprint(
|
||||
option, c.separator, tValue.v, "\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
c.RemoveOption(section, option)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = buf.WriteString("\n")
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user