use go modules
This commit is contained in:
		
							
								
								
									
										15
									
								
								vendor/github.com/spf13/viper/.editorconfig
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/spf13/viper/.editorconfig
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
[*]
 | 
			
		||||
charset = utf-8
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
indent_size = 4
 | 
			
		||||
indent_style = space
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
 | 
			
		||||
[*.go]
 | 
			
		||||
indent_style = tab
 | 
			
		||||
 | 
			
		||||
[{Makefile,*.mk}]
 | 
			
		||||
indent_style = tab
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/spf13/viper/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/spf13/viper/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
/.idea/
 | 
			
		||||
/bin/
 | 
			
		||||
/build/
 | 
			
		||||
/var/
 | 
			
		||||
/vendor/
 | 
			
		||||
							
								
								
									
										96
									
								
								vendor/github.com/spf13/viper/.golangci.yaml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								vendor/github.com/spf13/viper/.golangci.yaml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
			
		||||
run:
 | 
			
		||||
    timeout: 5m
 | 
			
		||||
 | 
			
		||||
linters-settings:
 | 
			
		||||
    gci:
 | 
			
		||||
        sections:
 | 
			
		||||
            - standard
 | 
			
		||||
            - default
 | 
			
		||||
            - prefix(github.com/spf13/viper)
 | 
			
		||||
    golint:
 | 
			
		||||
        min-confidence: 0
 | 
			
		||||
    goimports:
 | 
			
		||||
        local-prefixes: github.com/spf13/viper
 | 
			
		||||
 | 
			
		||||
linters:
 | 
			
		||||
    disable-all: true
 | 
			
		||||
    enable:
 | 
			
		||||
        - bodyclose
 | 
			
		||||
        - deadcode
 | 
			
		||||
        - dogsled
 | 
			
		||||
        - dupl
 | 
			
		||||
        - durationcheck
 | 
			
		||||
        - exhaustive
 | 
			
		||||
        - exportloopref
 | 
			
		||||
        - gci
 | 
			
		||||
        - gofmt
 | 
			
		||||
        - gofumpt
 | 
			
		||||
        - goimports
 | 
			
		||||
        - gomoddirectives
 | 
			
		||||
        - goprintffuncname
 | 
			
		||||
        - govet
 | 
			
		||||
        - importas
 | 
			
		||||
        - ineffassign
 | 
			
		||||
        - makezero
 | 
			
		||||
        - misspell
 | 
			
		||||
        - nakedret
 | 
			
		||||
        - nilerr
 | 
			
		||||
        - noctx
 | 
			
		||||
        - nolintlint
 | 
			
		||||
        - prealloc
 | 
			
		||||
        - predeclared
 | 
			
		||||
        - revive
 | 
			
		||||
        - rowserrcheck
 | 
			
		||||
        - sqlclosecheck
 | 
			
		||||
        - staticcheck
 | 
			
		||||
        - structcheck
 | 
			
		||||
        - stylecheck
 | 
			
		||||
        - tparallel
 | 
			
		||||
        - typecheck
 | 
			
		||||
        - unconvert
 | 
			
		||||
        - unparam
 | 
			
		||||
        - unused
 | 
			
		||||
        - varcheck
 | 
			
		||||
        - wastedassign
 | 
			
		||||
        - whitespace
 | 
			
		||||
 | 
			
		||||
        # fixme
 | 
			
		||||
        # - cyclop
 | 
			
		||||
        # - errcheck
 | 
			
		||||
        # - errorlint
 | 
			
		||||
        # - exhaustivestruct
 | 
			
		||||
        # - forbidigo
 | 
			
		||||
        # - forcetypeassert
 | 
			
		||||
        # - gochecknoglobals
 | 
			
		||||
        # - gochecknoinits
 | 
			
		||||
        # - gocognit
 | 
			
		||||
        # - goconst
 | 
			
		||||
        # - gocritic
 | 
			
		||||
        # - gocyclo
 | 
			
		||||
        # - godot
 | 
			
		||||
        # - gosec
 | 
			
		||||
        # - gosimple
 | 
			
		||||
        # - ifshort
 | 
			
		||||
        # - lll
 | 
			
		||||
        # - nlreturn
 | 
			
		||||
        # - paralleltest
 | 
			
		||||
        # - scopelint
 | 
			
		||||
        # - thelper
 | 
			
		||||
        # - wrapcheck
 | 
			
		||||
 | 
			
		||||
        # unused
 | 
			
		||||
        # - depguard
 | 
			
		||||
        # - goheader
 | 
			
		||||
        # - gomodguard
 | 
			
		||||
 | 
			
		||||
        # don't enable:
 | 
			
		||||
        # - asciicheck
 | 
			
		||||
        # - funlen
 | 
			
		||||
        # - godox
 | 
			
		||||
        # - goerr113
 | 
			
		||||
        # - gomnd
 | 
			
		||||
        # - interfacer
 | 
			
		||||
        # - maligned
 | 
			
		||||
        # - nestif
 | 
			
		||||
        # - testpackage
 | 
			
		||||
        # - wsl
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/spf13/viper/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/spf13/viper/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2014 Steve Francia
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										76
									
								
								vendor/github.com/spf13/viper/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/spf13/viper/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
			
		||||
# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
 | 
			
		||||
 | 
			
		||||
OS = $(shell uname | tr A-Z a-z)
 | 
			
		||||
export PATH := $(abspath bin/):${PATH}
 | 
			
		||||
 | 
			
		||||
# Build variables
 | 
			
		||||
BUILD_DIR ?= build
 | 
			
		||||
export CGO_ENABLED ?= 0
 | 
			
		||||
export GOOS = $(shell go env GOOS)
 | 
			
		||||
ifeq (${VERBOSE}, 1)
 | 
			
		||||
ifeq ($(filter -v,${GOARGS}),)
 | 
			
		||||
	GOARGS += -v
 | 
			
		||||
endif
 | 
			
		||||
TEST_FORMAT = short-verbose
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Dependency versions
 | 
			
		||||
GOTESTSUM_VERSION = 1.8.0
 | 
			
		||||
GOLANGCI_VERSION = 1.49.0
 | 
			
		||||
 | 
			
		||||
# Add the ability to override some variables
 | 
			
		||||
# Use with care
 | 
			
		||||
-include override.mk
 | 
			
		||||
 | 
			
		||||
.PHONY: clear
 | 
			
		||||
clear: ## Clear the working area and the project
 | 
			
		||||
	rm -rf bin/
 | 
			
		||||
 | 
			
		||||
.PHONY: check
 | 
			
		||||
check: test lint ## Run tests and linters
 | 
			
		||||
 | 
			
		||||
bin/gotestsum: bin/gotestsum-${GOTESTSUM_VERSION}
 | 
			
		||||
	@ln -sf gotestsum-${GOTESTSUM_VERSION} bin/gotestsum
 | 
			
		||||
bin/gotestsum-${GOTESTSUM_VERSION}:
 | 
			
		||||
	@mkdir -p bin
 | 
			
		||||
	curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum-${GOTESTSUM_VERSION} && chmod +x ./bin/gotestsum-${GOTESTSUM_VERSION}
 | 
			
		||||
 | 
			
		||||
TEST_PKGS ?= ./...
 | 
			
		||||
.PHONY: test
 | 
			
		||||
test: TEST_FORMAT ?= short
 | 
			
		||||
test: SHELL = /bin/bash
 | 
			
		||||
test: export CGO_ENABLED=1
 | 
			
		||||
test: bin/gotestsum ## Run tests
 | 
			
		||||
	@mkdir -p ${BUILD_DIR}
 | 
			
		||||
	bin/gotestsum --no-summary=skipped --junitfile ${BUILD_DIR}/coverage.xml --format ${TEST_FORMAT} -- -race -coverprofile=${BUILD_DIR}/coverage.txt -covermode=atomic $(filter-out -v,${GOARGS}) $(if ${TEST_PKGS},${TEST_PKGS},./...)
 | 
			
		||||
 | 
			
		||||
bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION}
 | 
			
		||||
	@ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint
 | 
			
		||||
bin/golangci-lint-${GOLANGCI_VERSION}:
 | 
			
		||||
	@mkdir -p bin
 | 
			
		||||
	curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION}
 | 
			
		||||
	@mv bin/golangci-lint "$@"
 | 
			
		||||
 | 
			
		||||
.PHONY: lint
 | 
			
		||||
lint: bin/golangci-lint ## Run linter
 | 
			
		||||
	bin/golangci-lint run
 | 
			
		||||
 | 
			
		||||
.PHONY: fix
 | 
			
		||||
fix: bin/golangci-lint ## Fix lint violations
 | 
			
		||||
	bin/golangci-lint run --fix
 | 
			
		||||
 | 
			
		||||
# Add custom targets here
 | 
			
		||||
-include custom.mk
 | 
			
		||||
 | 
			
		||||
.PHONY: list
 | 
			
		||||
list: ## List all make targets
 | 
			
		||||
	@${MAKE} -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | sort
 | 
			
		||||
 | 
			
		||||
.PHONY: help
 | 
			
		||||
.DEFAULT_GOAL := help
 | 
			
		||||
help:
 | 
			
		||||
	@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
 | 
			
		||||
 | 
			
		||||
# Variable outputting/exporting rules
 | 
			
		||||
var-%: ; @echo $($*)
 | 
			
		||||
varexport-%: ; @echo $*=$($*)
 | 
			
		||||
							
								
								
									
										881
									
								
								vendor/github.com/spf13/viper/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										881
									
								
								vendor/github.com/spf13/viper/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,881 @@
 | 
			
		||||
> ## Viper v2 feedback
 | 
			
		||||
> Viper is heading towards v2 and we would love to hear what _**you**_ would like to see in it. Share your thoughts here: https://forms.gle/R6faU74qPRPAzchZ9
 | 
			
		||||
>
 | 
			
		||||
> **Thank you!**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
[](https://github.com/avelino/awesome-go#configuration)
 | 
			
		||||
[](https://repl.it/@sagikazarmark/Viper-example#main.go)
 | 
			
		||||
 | 
			
		||||
[](https://github.com/spf13/viper/actions?query=workflow%3ACI)
 | 
			
		||||
[](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
			
		||||
[](https://goreportcard.com/report/github.com/spf13/viper)
 | 
			
		||||

 | 
			
		||||
[](https://pkg.go.dev/mod/github.com/spf13/viper)
 | 
			
		||||
 | 
			
		||||
**Go configuration with fangs!**
 | 
			
		||||
 | 
			
		||||
Many Go projects are built using Viper including:
 | 
			
		||||
 | 
			
		||||
* [Hugo](http://gohugo.io)
 | 
			
		||||
* [EMC RexRay](http://rexray.readthedocs.org/en/stable/)
 | 
			
		||||
* [Imgur’s Incus](https://github.com/Imgur/incus)
 | 
			
		||||
* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
 | 
			
		||||
* [Docker Notary](https://github.com/docker/Notary)
 | 
			
		||||
* [BloomApi](https://www.bloomapi.com/)
 | 
			
		||||
* [doctl](https://github.com/digitalocean/doctl)
 | 
			
		||||
* [Clairctl](https://github.com/jgsqware/clairctl)
 | 
			
		||||
* [Mercure](https://mercure.rocks)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Install
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
go get github.com/spf13/viper
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Note:** Viper uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## What is Viper?
 | 
			
		||||
 | 
			
		||||
Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed
 | 
			
		||||
to work within an application, and can handle all types of configuration needs
 | 
			
		||||
and formats. It supports:
 | 
			
		||||
 | 
			
		||||
* setting defaults
 | 
			
		||||
* reading from JSON, TOML, YAML, HCL, envfile and Java properties config files
 | 
			
		||||
* live watching and re-reading of config files (optional)
 | 
			
		||||
* reading from environment variables
 | 
			
		||||
* reading from remote config systems (etcd or Consul), and watching changes
 | 
			
		||||
* reading from command line flags
 | 
			
		||||
* reading from buffer
 | 
			
		||||
* setting explicit values
 | 
			
		||||
 | 
			
		||||
Viper can be thought of as a registry for all of your applications configuration needs.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Why Viper?
 | 
			
		||||
 | 
			
		||||
When building a modern application, you don’t want to worry about
 | 
			
		||||
configuration file formats; you want to focus on building awesome software.
 | 
			
		||||
Viper is here to help with that.
 | 
			
		||||
 | 
			
		||||
Viper does the following for you:
 | 
			
		||||
 | 
			
		||||
1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, INI, envfile or Java properties formats.
 | 
			
		||||
2. Provide a mechanism to set default values for your different configuration options.
 | 
			
		||||
3. Provide a mechanism to set override values for options specified through command line flags.
 | 
			
		||||
4. Provide an alias system to easily rename parameters without breaking existing code.
 | 
			
		||||
5. Make it easy to tell the difference between when a user has provided a command line or config file which is the same as the default.
 | 
			
		||||
 | 
			
		||||
Viper uses the following precedence order. Each item takes precedence over the item below it:
 | 
			
		||||
 | 
			
		||||
 * explicit call to `Set`
 | 
			
		||||
 * flag
 | 
			
		||||
 * env
 | 
			
		||||
 * config
 | 
			
		||||
 * key/value store
 | 
			
		||||
 * default
 | 
			
		||||
 | 
			
		||||
**Important:** Viper configuration keys are case insensitive.
 | 
			
		||||
There are ongoing discussions about making that optional.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Putting Values into Viper
 | 
			
		||||
 | 
			
		||||
### Establishing Defaults
 | 
			
		||||
 | 
			
		||||
A good configuration system will support default values. A default value is not
 | 
			
		||||
required for a key, but it’s useful in the event that a key hasn't been set via
 | 
			
		||||
config file, environment variable, remote configuration or flag.
 | 
			
		||||
 | 
			
		||||
Examples:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.SetDefault("ContentDir", "content")
 | 
			
		||||
viper.SetDefault("LayoutDir", "layouts")
 | 
			
		||||
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Reading Config Files
 | 
			
		||||
 | 
			
		||||
Viper requires minimal configuration so it knows where to look for config files.
 | 
			
		||||
Viper supports JSON, TOML, YAML, HCL, INI, envfile and Java Properties files. Viper can search multiple paths, but
 | 
			
		||||
currently a single Viper instance only supports a single configuration file.
 | 
			
		||||
Viper does not default to any configuration search paths leaving defaults decision
 | 
			
		||||
to an application.
 | 
			
		||||
 | 
			
		||||
Here is an example of how to use Viper to search for and read a configuration file.
 | 
			
		||||
None of the specific paths are required, but at least one path should be provided
 | 
			
		||||
where a configuration file is expected.
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.SetConfigName("config") // name of config file (without extension)
 | 
			
		||||
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
 | 
			
		||||
viper.AddConfigPath("/etc/appname/")   // path to look for the config file in
 | 
			
		||||
viper.AddConfigPath("$HOME/.appname")  // call multiple times to add many search paths
 | 
			
		||||
viper.AddConfigPath(".")               // optionally look for config in the working directory
 | 
			
		||||
err := viper.ReadInConfig() // Find and read the config file
 | 
			
		||||
if err != nil { // Handle errors reading the config file
 | 
			
		||||
	panic(fmt.Errorf("fatal error config file: %w", err))
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can handle the specific case where no config file is found like this:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
if err := viper.ReadInConfig(); err != nil {
 | 
			
		||||
	if _, ok := err.(viper.ConfigFileNotFoundError); ok {
 | 
			
		||||
		// Config file not found; ignore error if desired
 | 
			
		||||
	} else {
 | 
			
		||||
		// Config file was found but another error was produced
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Config file found and successfully parsed
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmaticaly. For those configuration files that lie in the home of the user without any extension like `.bashrc`
 | 
			
		||||
 | 
			
		||||
### Writing Config Files
 | 
			
		||||
 | 
			
		||||
Reading from config files is useful, but at times you want to store all modifications made at run time.
 | 
			
		||||
For that, a bunch of commands are available, each with its own purpose:
 | 
			
		||||
 | 
			
		||||
* WriteConfig - writes the current viper configuration to the predefined path, if exists. Errors if no predefined path. Will overwrite the current config file, if it exists.
 | 
			
		||||
* SafeWriteConfig - writes the current viper configuration to the predefined path. Errors if no predefined path. Will not overwrite the current config file, if it exists.
 | 
			
		||||
* WriteConfigAs - writes the current viper configuration to the given filepath. Will overwrite the given file, if it exists.
 | 
			
		||||
* SafeWriteConfigAs - writes the current viper configuration to the given filepath. Will not overwrite the given file, if it exists.
 | 
			
		||||
 | 
			
		||||
As a rule of the thumb, everything marked with safe won't overwrite any file, but just create if not existent, whilst the default behavior is to create or truncate.
 | 
			
		||||
 | 
			
		||||
A small examples section:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.WriteConfig() // writes current config to predefined path set by 'viper.AddConfigPath()' and 'viper.SetConfigName'
 | 
			
		||||
viper.SafeWriteConfig()
 | 
			
		||||
viper.WriteConfigAs("/path/to/my/.config")
 | 
			
		||||
viper.SafeWriteConfigAs("/path/to/my/.config") // will error since it has already been written
 | 
			
		||||
viper.SafeWriteConfigAs("/path/to/my/.other_config")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Watching and re-reading config files
 | 
			
		||||
 | 
			
		||||
Viper supports the ability to have your application live read a config file while running.
 | 
			
		||||
 | 
			
		||||
Gone are the days of needing to restart a server to have a config take effect,
 | 
			
		||||
viper powered applications can read an update to a config file while running and
 | 
			
		||||
not miss a beat.
 | 
			
		||||
 | 
			
		||||
Simply tell the viper instance to watchConfig.
 | 
			
		||||
Optionally you can provide a function for Viper to run each time a change occurs.
 | 
			
		||||
 | 
			
		||||
**Make sure you add all of the configPaths prior to calling `WatchConfig()`**
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.OnConfigChange(func(e fsnotify.Event) {
 | 
			
		||||
	fmt.Println("Config file changed:", e.Name)
 | 
			
		||||
})
 | 
			
		||||
viper.WatchConfig()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Reading Config from io.Reader
 | 
			
		||||
 | 
			
		||||
Viper predefines many configuration sources such as files, environment
 | 
			
		||||
variables, flags, and remote K/V store, but you are not bound to them. You can
 | 
			
		||||
also implement your own required configuration source and feed it to viper.
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.SetConfigType("yaml") // or viper.SetConfigType("YAML")
 | 
			
		||||
 | 
			
		||||
// any approach to require this configuration into your program.
 | 
			
		||||
var yamlExample = []byte(`
 | 
			
		||||
Hacker: true
 | 
			
		||||
name: steve
 | 
			
		||||
hobbies:
 | 
			
		||||
- skateboarding
 | 
			
		||||
- snowboarding
 | 
			
		||||
- go
 | 
			
		||||
clothing:
 | 
			
		||||
  jacket: leather
 | 
			
		||||
  trousers: denim
 | 
			
		||||
age: 35
 | 
			
		||||
eyes : brown
 | 
			
		||||
beard: true
 | 
			
		||||
`)
 | 
			
		||||
 | 
			
		||||
viper.ReadConfig(bytes.NewBuffer(yamlExample))
 | 
			
		||||
 | 
			
		||||
viper.Get("name") // this would be "steve"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Setting Overrides
 | 
			
		||||
 | 
			
		||||
These could be from a command line flag, or from your own application logic.
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.Set("Verbose", true)
 | 
			
		||||
viper.Set("LogFile", LogFile)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Registering and Using Aliases
 | 
			
		||||
 | 
			
		||||
Aliases permit a single value to be referenced by multiple keys
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.RegisterAlias("loud", "Verbose")
 | 
			
		||||
 | 
			
		||||
viper.Set("verbose", true) // same result as next line
 | 
			
		||||
viper.Set("loud", true)   // same result as prior line
 | 
			
		||||
 | 
			
		||||
viper.GetBool("loud") // true
 | 
			
		||||
viper.GetBool("verbose") // true
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Working with Environment Variables
 | 
			
		||||
 | 
			
		||||
Viper has full support for environment variables. This enables 12 factor
 | 
			
		||||
applications out of the box. There are five methods that exist to aid working
 | 
			
		||||
with ENV:
 | 
			
		||||
 | 
			
		||||
 * `AutomaticEnv()`
 | 
			
		||||
 * `BindEnv(string...) : error`
 | 
			
		||||
 * `SetEnvPrefix(string)`
 | 
			
		||||
 * `SetEnvKeyReplacer(string...) *strings.Replacer`
 | 
			
		||||
 * `AllowEmptyEnv(bool)`
 | 
			
		||||
 | 
			
		||||
_When working with ENV variables, it’s important to recognize that Viper
 | 
			
		||||
treats ENV variables as case sensitive._
 | 
			
		||||
 | 
			
		||||
Viper provides a mechanism to try to ensure that ENV variables are unique. By
 | 
			
		||||
using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from
 | 
			
		||||
the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
 | 
			
		||||
prefix.
 | 
			
		||||
 | 
			
		||||
`BindEnv` takes one or more parameters. The first parameter is the key name, the
 | 
			
		||||
rest are the name of the environment variables to bind to this key. If more than
 | 
			
		||||
one are provided, they will take precedence in the specified order. The name of
 | 
			
		||||
the environment variable is case sensitive. If the ENV variable name is not provided, then
 | 
			
		||||
Viper will automatically assume that the ENV variable matches the following format: prefix + "_" + the key name in ALL CAPS. When you explicitly provide the ENV variable name (the second parameter),
 | 
			
		||||
it **does not** automatically add the prefix. For example if the second parameter is "id",
 | 
			
		||||
Viper will look for the ENV variable "ID".
 | 
			
		||||
 | 
			
		||||
One important thing to recognize when working with ENV variables is that the
 | 
			
		||||
value will be read each time it is accessed. Viper does not fix the value when
 | 
			
		||||
the `BindEnv` is called.
 | 
			
		||||
 | 
			
		||||
`AutomaticEnv` is a powerful helper especially when combined with
 | 
			
		||||
`SetEnvPrefix`. When called, Viper will check for an environment variable any
 | 
			
		||||
time a `viper.Get` request is made. It will apply the following rules. It will
 | 
			
		||||
check for an environment variable with a name matching the key uppercased and
 | 
			
		||||
prefixed with the `EnvPrefix` if set.
 | 
			
		||||
 | 
			
		||||
`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env
 | 
			
		||||
keys to an extent. This is useful if you want to use `-` or something in your
 | 
			
		||||
`Get()` calls, but want your environmental variables to use `_` delimiters. An
 | 
			
		||||
example of using it can be found in `viper_test.go`.
 | 
			
		||||
 | 
			
		||||
Alternatively, you can use `EnvKeyReplacer` with `NewWithOptions` factory function.
 | 
			
		||||
Unlike `SetEnvKeyReplacer`, it accepts a `StringReplacer` interface allowing you to write custom string replacing logic.
 | 
			
		||||
 | 
			
		||||
By default empty environment variables are considered unset and will fall back to
 | 
			
		||||
the next configuration source. To treat empty environment variables as set, use
 | 
			
		||||
the `AllowEmptyEnv` method.
 | 
			
		||||
 | 
			
		||||
#### Env example
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
SetEnvPrefix("spf") // will be uppercased automatically
 | 
			
		||||
BindEnv("id")
 | 
			
		||||
 | 
			
		||||
os.Setenv("SPF_ID", "13") // typically done outside of the app
 | 
			
		||||
 | 
			
		||||
id := Get("id") // 13
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Working with Flags
 | 
			
		||||
 | 
			
		||||
Viper has the ability to bind to flags. Specifically, Viper supports `Pflags`
 | 
			
		||||
as used in the [Cobra](https://github.com/spf13/cobra) library.
 | 
			
		||||
 | 
			
		||||
Like `BindEnv`, the value is not set when the binding method is called, but when
 | 
			
		||||
it is accessed. This means you can bind as early as you want, even in an
 | 
			
		||||
`init()` function.
 | 
			
		||||
 | 
			
		||||
For individual flags, the `BindPFlag()` method provides this functionality.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
 | 
			
		||||
viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can also bind an existing set of pflags (pflag.FlagSet):
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
pflag.Int("flagname", 1234, "help message for flagname")
 | 
			
		||||
 | 
			
		||||
pflag.Parse()
 | 
			
		||||
viper.BindPFlags(pflag.CommandLine)
 | 
			
		||||
 | 
			
		||||
i := viper.GetInt("flagname") // retrieve values from viper instead of pflag
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude
 | 
			
		||||
the use of other packages that use the [flag](https://golang.org/pkg/flag/)
 | 
			
		||||
package from the standard library. The pflag package can handle the flags
 | 
			
		||||
defined for the flag package by importing these flags. This is accomplished
 | 
			
		||||
by a calling a convenience function provided by the pflag package called
 | 
			
		||||
AddGoFlagSet().
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"github.com/spf13/pflag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
 | 
			
		||||
	// using standard library "flag" package
 | 
			
		||||
	flag.Int("flagname", 1234, "help message for flagname")
 | 
			
		||||
 | 
			
		||||
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
 | 
			
		||||
	pflag.Parse()
 | 
			
		||||
	viper.BindPFlags(pflag.CommandLine)
 | 
			
		||||
 | 
			
		||||
	i := viper.GetInt("flagname") // retrieve value from viper
 | 
			
		||||
 | 
			
		||||
	// ...
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Flag interfaces
 | 
			
		||||
 | 
			
		||||
Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`.
 | 
			
		||||
 | 
			
		||||
`FlagValue` represents a single flag. This is a very simple example on how to implement this interface:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type myFlag struct {}
 | 
			
		||||
func (f myFlag) HasChanged() bool { return false }
 | 
			
		||||
func (f myFlag) Name() string { return "my-flag-name" }
 | 
			
		||||
func (f myFlag) ValueString() string { return "my-flag-value" }
 | 
			
		||||
func (f myFlag) ValueType() string { return "string" }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Once your flag implements this interface, you can simply tell Viper to bind it:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.BindFlagValue("my-flag-name", myFlag{})
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`FlagValueSet` represents a group of flags. This is a very simple example on how to implement this interface:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type myFlagSet struct {
 | 
			
		||||
	flags []myFlag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f myFlagSet) VisitAll(fn func(FlagValue)) {
 | 
			
		||||
	for _, flag := range flags {
 | 
			
		||||
		fn(flag)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Once your flag set implements this interface, you can simply tell Viper to bind it:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
fSet := myFlagSet{
 | 
			
		||||
	flags: []myFlag{myFlag{}, myFlag{}},
 | 
			
		||||
}
 | 
			
		||||
viper.BindFlagValues("my-flags", fSet)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Remote Key/Value Store Support
 | 
			
		||||
 | 
			
		||||
To enable remote support in Viper, do a blank import of the `viper/remote`
 | 
			
		||||
package:
 | 
			
		||||
 | 
			
		||||
`import _ "github.com/spf13/viper/remote"`
 | 
			
		||||
 | 
			
		||||
Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path
 | 
			
		||||
in a Key/Value store such as etcd or Consul.  These values take precedence over
 | 
			
		||||
default values, but are overridden by configuration values retrieved from disk,
 | 
			
		||||
flags, or environment variables.
 | 
			
		||||
 | 
			
		||||
Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve
 | 
			
		||||
configuration from the K/V store, which means that you can store your
 | 
			
		||||
configuration values encrypted and have them automatically decrypted if you have
 | 
			
		||||
the correct gpg keyring.  Encryption is optional.
 | 
			
		||||
 | 
			
		||||
You can use remote configuration in conjunction with local configuration, or
 | 
			
		||||
independently of it.
 | 
			
		||||
 | 
			
		||||
`crypt` has a command-line helper that you can use to put configurations in your
 | 
			
		||||
K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ go get github.com/bketelsen/crypt/bin/crypt
 | 
			
		||||
$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Confirm that your value was set:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ crypt get -plaintext /config/hugo.json
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
See the `crypt` documentation for examples of how to set encrypted values, or
 | 
			
		||||
how to use Consul.
 | 
			
		||||
 | 
			
		||||
### Remote Key/Value Store Example - Unencrypted
 | 
			
		||||
 | 
			
		||||
#### etcd
 | 
			
		||||
```go
 | 
			
		||||
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
 | 
			
		||||
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
 | 
			
		||||
err := viper.ReadRemoteConfig()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### etcd3
 | 
			
		||||
```go
 | 
			
		||||
viper.AddRemoteProvider("etcd3", "http://127.0.0.1:4001","/config/hugo.json")
 | 
			
		||||
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
 | 
			
		||||
err := viper.ReadRemoteConfig()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Consul
 | 
			
		||||
You need to set a key to Consul key/value storage with JSON value containing your desired config.
 | 
			
		||||
For example, create a Consul key/value store key `MY_CONSUL_KEY` with value:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "port": 8080,
 | 
			
		||||
    "hostname": "myhostname.com"
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")
 | 
			
		||||
viper.SetConfigType("json") // Need to explicitly set this to json
 | 
			
		||||
err := viper.ReadRemoteConfig()
 | 
			
		||||
 | 
			
		||||
fmt.Println(viper.Get("port")) // 8080
 | 
			
		||||
fmt.Println(viper.Get("hostname")) // myhostname.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Firestore
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document")
 | 
			
		||||
viper.SetConfigType("json") // Config's format: "json", "toml", "yaml", "yml"
 | 
			
		||||
err := viper.ReadRemoteConfig()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Of course, you're allowed to use `SecureRemoteProvider` also
 | 
			
		||||
 | 
			
		||||
### Remote Key/Value Store Example - Encrypted
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")
 | 
			
		||||
viper.SetConfigType("json") // because there is no file extension in a stream of bytes,  supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
 | 
			
		||||
err := viper.ReadRemoteConfig()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Watching Changes in etcd - Unencrypted
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// alternatively, you can create a new viper instance.
 | 
			
		||||
var runtime_viper = viper.New()
 | 
			
		||||
 | 
			
		||||
runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
 | 
			
		||||
runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
 | 
			
		||||
 | 
			
		||||
// read from remote config the first time.
 | 
			
		||||
err := runtime_viper.ReadRemoteConfig()
 | 
			
		||||
 | 
			
		||||
// unmarshal config
 | 
			
		||||
runtime_viper.Unmarshal(&runtime_conf)
 | 
			
		||||
 | 
			
		||||
// open a goroutine to watch remote changes forever
 | 
			
		||||
go func(){
 | 
			
		||||
	for {
 | 
			
		||||
		time.Sleep(time.Second * 5) // delay after each request
 | 
			
		||||
 | 
			
		||||
		// currently, only tested with etcd support
 | 
			
		||||
		err := runtime_viper.WatchRemoteConfig()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Errorf("unable to read remote config: %v", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// unmarshal new config into our runtime config struct. you can also use channel
 | 
			
		||||
		// to implement a signal to notify the system of the changes
 | 
			
		||||
		runtime_viper.Unmarshal(&runtime_conf)
 | 
			
		||||
	}
 | 
			
		||||
}()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Getting Values From Viper
 | 
			
		||||
 | 
			
		||||
In Viper, there are a few ways to get a value depending on the value’s type.
 | 
			
		||||
The following functions and methods exist:
 | 
			
		||||
 | 
			
		||||
 * `Get(key string) : interface{}`
 | 
			
		||||
 * `GetBool(key string) : bool`
 | 
			
		||||
 * `GetFloat64(key string) : float64`
 | 
			
		||||
 * `GetInt(key string) : int`
 | 
			
		||||
 * `GetIntSlice(key string) : []int`
 | 
			
		||||
 * `GetString(key string) : string`
 | 
			
		||||
 * `GetStringMap(key string) : map[string]interface{}`
 | 
			
		||||
 * `GetStringMapString(key string) : map[string]string`
 | 
			
		||||
 * `GetStringSlice(key string) : []string`
 | 
			
		||||
 * `GetTime(key string) : time.Time`
 | 
			
		||||
 * `GetDuration(key string) : time.Duration`
 | 
			
		||||
 * `IsSet(key string) : bool`
 | 
			
		||||
 * `AllSettings() : map[string]interface{}`
 | 
			
		||||
 | 
			
		||||
One important thing to recognize is that each Get function will return a zero
 | 
			
		||||
value if it’s not found. To check if a given key exists, the `IsSet()` method
 | 
			
		||||
has been provided.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
```go
 | 
			
		||||
viper.GetString("logfile") // case-insensitive Setting & Getting
 | 
			
		||||
if viper.GetBool("verbose") {
 | 
			
		||||
	fmt.Println("verbose enabled")
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
### Accessing nested keys
 | 
			
		||||
 | 
			
		||||
The accessor methods also accept formatted paths to deeply nested keys. For
 | 
			
		||||
example, if the following JSON file is loaded:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "host": {
 | 
			
		||||
        "address": "localhost",
 | 
			
		||||
        "port": 5799
 | 
			
		||||
    },
 | 
			
		||||
    "datastore": {
 | 
			
		||||
        "metric": {
 | 
			
		||||
            "host": "127.0.0.1",
 | 
			
		||||
            "port": 3099
 | 
			
		||||
        },
 | 
			
		||||
        "warehouse": {
 | 
			
		||||
            "host": "198.0.0.1",
 | 
			
		||||
            "port": 2112
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Viper can access a nested field by passing a `.` delimited path of keys:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
GetString("datastore.metric.host") // (returns "127.0.0.1")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This obeys the precedence rules established above; the search for the path
 | 
			
		||||
will cascade through the remaining configuration registries until found.
 | 
			
		||||
 | 
			
		||||
For example, given this configuration file, both `datastore.metric.host` and
 | 
			
		||||
`datastore.metric.port` are already defined (and may be overridden). If in addition
 | 
			
		||||
`datastore.metric.protocol` was defined in the defaults, Viper would also find it.
 | 
			
		||||
 | 
			
		||||
However, if `datastore.metric` was overridden (by a flag, an environment variable,
 | 
			
		||||
the `Set()` method, …) with an immediate value, then all sub-keys of
 | 
			
		||||
`datastore.metric` become undefined, they are “shadowed” by the higher-priority
 | 
			
		||||
configuration level.
 | 
			
		||||
 | 
			
		||||
Viper can access array indices by using numbers in the path. For example:
 | 
			
		||||
 | 
			
		||||
```jsonc
 | 
			
		||||
{
 | 
			
		||||
    "host": {
 | 
			
		||||
        "address": "localhost",
 | 
			
		||||
        "ports": [
 | 
			
		||||
            5799,
 | 
			
		||||
            6029
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    "datastore": {
 | 
			
		||||
        "metric": {
 | 
			
		||||
            "host": "127.0.0.1",
 | 
			
		||||
            "port": 3099
 | 
			
		||||
        },
 | 
			
		||||
        "warehouse": {
 | 
			
		||||
            "host": "198.0.0.1",
 | 
			
		||||
            "port": 2112
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GetInt("host.ports.1") // returns 6029
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Lastly, if there exists a key that matches the delimited key path, its value
 | 
			
		||||
will be returned instead. E.g.
 | 
			
		||||
 | 
			
		||||
```jsonc
 | 
			
		||||
{
 | 
			
		||||
    "datastore.metric.host": "0.0.0.0",
 | 
			
		||||
    "host": {
 | 
			
		||||
        "address": "localhost",
 | 
			
		||||
        "port": 5799
 | 
			
		||||
    },
 | 
			
		||||
    "datastore": {
 | 
			
		||||
        "metric": {
 | 
			
		||||
            "host": "127.0.0.1",
 | 
			
		||||
            "port": 3099
 | 
			
		||||
        },
 | 
			
		||||
        "warehouse": {
 | 
			
		||||
            "host": "198.0.0.1",
 | 
			
		||||
            "port": 2112
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GetString("datastore.metric.host") // returns "0.0.0.0"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Extracting a sub-tree
 | 
			
		||||
 | 
			
		||||
When developing reusable modules, it's often useful to extract a subset of the configuration
 | 
			
		||||
and pass it to a module. This way the module can be instantiated more than once, with different configurations.
 | 
			
		||||
 | 
			
		||||
For example, an application might use multiple different cache stores for different purposes:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
cache:
 | 
			
		||||
  cache1:
 | 
			
		||||
    max-items: 100
 | 
			
		||||
    item-size: 64
 | 
			
		||||
  cache2:
 | 
			
		||||
    max-items: 200
 | 
			
		||||
    item-size: 80
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
We could pass the cache name to a module (eg. `NewCache("cache1")`),
 | 
			
		||||
but it would require weird concatenation for accessing config keys and would be less separated from the global config.
 | 
			
		||||
 | 
			
		||||
So instead of doing that let's pass a Viper instance to the constructor that represents a subset of the configuration:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
cache1Config := viper.Sub("cache.cache1")
 | 
			
		||||
if cache1Config == nil { // Sub returns nil if the key cannot be found
 | 
			
		||||
	panic("cache configuration not found")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cache1 := NewCache(cache1Config)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Note:** Always check the return value of `Sub`. It returns `nil` if a key cannot be found.
 | 
			
		||||
 | 
			
		||||
Internally, the `NewCache` function can address `max-items` and `item-size` keys directly:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
func NewCache(v *Viper) *Cache {
 | 
			
		||||
	return &Cache{
 | 
			
		||||
		MaxItems: v.GetInt("max-items"),
 | 
			
		||||
		ItemSize: v.GetInt("item-size"),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The resulting code is easy to test, since it's decoupled from the main config structure,
 | 
			
		||||
and easier to reuse (for the same reason).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Unmarshaling
 | 
			
		||||
 | 
			
		||||
You also have the option of Unmarshaling all or a specific value to a struct, map,
 | 
			
		||||
etc.
 | 
			
		||||
 | 
			
		||||
There are two methods to do this:
 | 
			
		||||
 | 
			
		||||
 * `Unmarshal(rawVal interface{}) : error`
 | 
			
		||||
 * `UnmarshalKey(key string, rawVal interface{}) : error`
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type config struct {
 | 
			
		||||
	Port int
 | 
			
		||||
	Name string
 | 
			
		||||
	PathMap string `mapstructure:"path_map"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var C config
 | 
			
		||||
 | 
			
		||||
err := viper.Unmarshal(&C)
 | 
			
		||||
if err != nil {
 | 
			
		||||
	t.Fatalf("unable to decode into struct, %v", err)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If you want to unmarshal configuration where the keys themselves contain dot (the default key delimiter),
 | 
			
		||||
you have to change the delimiter:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
v := viper.NewWithOptions(viper.KeyDelimiter("::"))
 | 
			
		||||
 | 
			
		||||
v.SetDefault("chart::values", map[string]interface{}{
 | 
			
		||||
	"ingress": map[string]interface{}{
 | 
			
		||||
		"annotations": map[string]interface{}{
 | 
			
		||||
			"traefik.frontend.rule.type":                 "PathPrefix",
 | 
			
		||||
			"traefik.ingress.kubernetes.io/ssl-redirect": "true",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
type config struct {
 | 
			
		||||
	Chart struct{
 | 
			
		||||
		Values map[string]interface{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var C config
 | 
			
		||||
 | 
			
		||||
v.Unmarshal(&C)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Viper also supports unmarshaling into embedded structs:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
/*
 | 
			
		||||
Example config:
 | 
			
		||||
 | 
			
		||||
module:
 | 
			
		||||
    enabled: true
 | 
			
		||||
    token: 89h3f98hbwf987h3f98wenf89ehf
 | 
			
		||||
*/
 | 
			
		||||
type config struct {
 | 
			
		||||
	Module struct {
 | 
			
		||||
		Enabled bool
 | 
			
		||||
 | 
			
		||||
		moduleConfig `mapstructure:",squash"`
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// moduleConfig could be in a module specific package
 | 
			
		||||
type moduleConfig struct {
 | 
			
		||||
	Token string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var C config
 | 
			
		||||
 | 
			
		||||
err := viper.Unmarshal(&C)
 | 
			
		||||
if err != nil {
 | 
			
		||||
	t.Fatalf("unable to decode into struct, %v", err)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default.
 | 
			
		||||
 | 
			
		||||
### Decoding custom formats
 | 
			
		||||
 | 
			
		||||
A frequently requested feature for Viper is adding more value formats and decoders.
 | 
			
		||||
For example, parsing character (dot, comma, semicolon, etc) separated strings into slices.
 | 
			
		||||
 | 
			
		||||
This is already available in Viper using mapstructure decode hooks.
 | 
			
		||||
 | 
			
		||||
Read more about the details in [this blog post](https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/).
 | 
			
		||||
 | 
			
		||||
### Marshalling to string
 | 
			
		||||
 | 
			
		||||
You may need to marshal all the settings held in viper into a string rather than write them to a file.
 | 
			
		||||
You can use your favorite format's marshaller with the config returned by `AllSettings()`.
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
import (
 | 
			
		||||
	yaml "gopkg.in/yaml.v2"
 | 
			
		||||
	// ...
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func yamlStringSettings() string {
 | 
			
		||||
	c := viper.AllSettings()
 | 
			
		||||
	bs, err := yaml.Marshal(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("unable to marshal config to YAML: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return string(bs)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Viper or Vipers?
 | 
			
		||||
 | 
			
		||||
Viper comes ready to use out of the box. There is no configuration or
 | 
			
		||||
initialization needed to begin using Viper. Since most applications will want
 | 
			
		||||
to use a single central repository for their configuration, the viper package
 | 
			
		||||
provides this. It is similar to a singleton.
 | 
			
		||||
 | 
			
		||||
In all of the examples above, they demonstrate using viper in its singleton
 | 
			
		||||
style approach.
 | 
			
		||||
 | 
			
		||||
### Working with multiple vipers
 | 
			
		||||
 | 
			
		||||
You can also create many different vipers for use in your application. Each will
 | 
			
		||||
have its own unique set of configurations and values. Each can read from a
 | 
			
		||||
different config file, key value store, etc. All of the functions that viper
 | 
			
		||||
package supports are mirrored as methods on a viper.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
x := viper.New()
 | 
			
		||||
y := viper.New()
 | 
			
		||||
 | 
			
		||||
x.SetDefault("ContentDir", "content")
 | 
			
		||||
y.SetDefault("ContentDir", "foobar")
 | 
			
		||||
 | 
			
		||||
//...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
When working with multiple vipers, it is up to the user to keep track of the
 | 
			
		||||
different vipers.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Q & A
 | 
			
		||||
 | 
			
		||||
### Why is it called “Viper”?
 | 
			
		||||
 | 
			
		||||
A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe))
 | 
			
		||||
to [Cobra](https://github.com/spf13/cobra). While both can operate completely
 | 
			
		||||
independently, together they make a powerful pair to handle much of your
 | 
			
		||||
application foundation needs.
 | 
			
		||||
 | 
			
		||||
### Why is it called “Cobra”?
 | 
			
		||||
 | 
			
		||||
Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)?
 | 
			
		||||
 | 
			
		||||
### Does Viper support case sensitive keys?
 | 
			
		||||
 | 
			
		||||
**tl;dr:** No.
 | 
			
		||||
 | 
			
		||||
Viper merges configuration from various sources, many of which are either case insensitive or uses different casing than the rest of the sources (eg. env vars).
 | 
			
		||||
In order to provide the best experience when using multiple sources, the decision has been made to make all keys case insensitive.
 | 
			
		||||
 | 
			
		||||
There has been several attempts to implement case sensitivity, but unfortunately it's not that trivial. We might take a stab at implementing it in [Viper v2](https://github.com/spf13/viper/issues/772), but despite the initial noise, it does not seem to be requested that much.
 | 
			
		||||
 | 
			
		||||
You can vote for case sensitivity by filling out this feedback form: https://forms.gle/R6faU74qPRPAzchZ9
 | 
			
		||||
 | 
			
		||||
### Is it safe to concurrently read and write to a viper?
 | 
			
		||||
 | 
			
		||||
No, you will need to synchronize access to the viper yourself (for example by using the `sync` package). Concurrent reads and writes can cause a panic.
 | 
			
		||||
 | 
			
		||||
## Troubleshooting
 | 
			
		||||
 | 
			
		||||
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/spf13/viper/TROUBLESHOOTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/spf13/viper/TROUBLESHOOTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
# Troubleshooting
 | 
			
		||||
 | 
			
		||||
## Unmarshaling doesn't work
 | 
			
		||||
 | 
			
		||||
The most common reason for this issue is improper use of struct tags (eg. `yaml` or `json`). Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. Please refer to the library's documentation for using other struct tags.
 | 
			
		||||
 | 
			
		||||
## Cannot find package
 | 
			
		||||
 | 
			
		||||
Viper installation seems to fail a lot lately with the following (or a similar) error:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
cannot find package "github.com/hashicorp/hcl/tree/hcl1" in any of:
 | 
			
		||||
/usr/local/Cellar/go/1.15.7_1/libexec/src/github.com/hashicorp/hcl/tree/hcl1 (from $GOROOT)
 | 
			
		||||
/Users/user/go/src/github.com/hashicorp/hcl/tree/hcl1 (from $GOPATH)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
As the error message suggests, Go tries to look up dependencies in `GOPATH` mode (as it's commonly called) from the `GOPATH`.
 | 
			
		||||
Viper opted to use [Go Modules](https://github.com/golang/go/wiki/Modules) to manage its dependencies. While in many cases the two methods are interchangeable, once a dependency releases new (major) versions, `GOPATH` mode is no longer able to decide which version to use, so it'll either use one that's already present or pick a version (usually the `master` branch).
 | 
			
		||||
 | 
			
		||||
The solution is easy: switch to using Go Modules.
 | 
			
		||||
Please refer to the [wiki](https://github.com/golang/go/wiki/Modules) on how to do that.
 | 
			
		||||
 | 
			
		||||
**tl;dr* `export GO111MODULE=on`
 | 
			
		||||
 | 
			
		||||
## Unquoted 'y' and 'n' characters get replaced with _true_ and _false_ when reading a YAML file
 | 
			
		||||
 | 
			
		||||
This is a YAML 1.1 feature according to [go-yaml/yaml#740](https://github.com/go-yaml/yaml/issues/740).
 | 
			
		||||
 | 
			
		||||
Potential solutions are:
 | 
			
		||||
 | 
			
		||||
1. Quoting values resolved as boolean
 | 
			
		||||
1. Upgrading to YAML v3 (for the time being this is possible by passing the `viper_yaml3` tag to your build)
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/spf13/viper/experimental_logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/spf13/viper/experimental_logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
//go:build viper_logger
 | 
			
		||||
// +build viper_logger
 | 
			
		||||
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
// WithLogger sets a custom logger.
 | 
			
		||||
func WithLogger(l Logger) Option {
 | 
			
		||||
	return optionFunc(func(v *Viper) {
 | 
			
		||||
		v.logger = l
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								vendor/github.com/spf13/viper/flags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/spf13/viper/flags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
import "github.com/spf13/pflag"
 | 
			
		||||
 | 
			
		||||
// FlagValueSet is an interface that users can implement
 | 
			
		||||
// to bind a set of flags to viper.
 | 
			
		||||
type FlagValueSet interface {
 | 
			
		||||
	VisitAll(fn func(FlagValue))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FlagValue is an interface that users can implement
 | 
			
		||||
// to bind different flags to viper.
 | 
			
		||||
type FlagValue interface {
 | 
			
		||||
	HasChanged() bool
 | 
			
		||||
	Name() string
 | 
			
		||||
	ValueString() string
 | 
			
		||||
	ValueType() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pflagValueSet is a wrapper around *pflag.ValueSet
 | 
			
		||||
// that implements FlagValueSet.
 | 
			
		||||
type pflagValueSet struct {
 | 
			
		||||
	flags *pflag.FlagSet
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VisitAll iterates over all *pflag.Flag inside the *pflag.FlagSet.
 | 
			
		||||
func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {
 | 
			
		||||
	p.flags.VisitAll(func(flag *pflag.Flag) {
 | 
			
		||||
		fn(pflagValue{flag})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pflagValue is a wrapper aroung *pflag.flag
 | 
			
		||||
// that implements FlagValue
 | 
			
		||||
type pflagValue struct {
 | 
			
		||||
	flag *pflag.Flag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasChanged returns whether the flag has changes or not.
 | 
			
		||||
func (p pflagValue) HasChanged() bool {
 | 
			
		||||
	return p.flag.Changed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name returns the name of the flag.
 | 
			
		||||
func (p pflagValue) Name() string {
 | 
			
		||||
	return p.flag.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValueString returns the value of the flag as a string.
 | 
			
		||||
func (p pflagValue) ValueString() string {
 | 
			
		||||
	return p.flag.Value.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValueType returns the type of the flag as a string.
 | 
			
		||||
func (p pflagValue) ValueType() string {
 | 
			
		||||
	return p.flag.Value.Type()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								vendor/github.com/spf13/viper/fs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/spf13/viper/fs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
//go:build go1.16 && finder
 | 
			
		||||
// +build go1.16,finder
 | 
			
		||||
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io/fs"
 | 
			
		||||
	"path"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type finder struct {
 | 
			
		||||
	paths      []string
 | 
			
		||||
	fileNames  []string
 | 
			
		||||
	extensions []string
 | 
			
		||||
 | 
			
		||||
	withoutExtension bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f finder) Find(fsys fs.FS) (string, error) {
 | 
			
		||||
	for _, searchPath := range f.paths {
 | 
			
		||||
		for _, fileName := range f.fileNames {
 | 
			
		||||
			for _, extension := range f.extensions {
 | 
			
		||||
				filePath := path.Join(searchPath, fileName+"."+extension)
 | 
			
		||||
 | 
			
		||||
				ok, err := fileExists(fsys, filePath)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if ok {
 | 
			
		||||
					return filePath, nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if f.withoutExtension {
 | 
			
		||||
				filePath := path.Join(searchPath, fileName)
 | 
			
		||||
 | 
			
		||||
				ok, err := fileExists(fsys, filePath)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if ok {
 | 
			
		||||
					return filePath, nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileExists(fsys fs.FS, filePath string) (bool, error) {
 | 
			
		||||
	fileInfo, err := fs.Stat(fsys, filePath)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return !fileInfo.IsDir(), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if errors.Is(err, fs.ErrNotExist) {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								vendor/github.com/spf13/viper/internal/encoding/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/spf13/viper/internal/encoding/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Decoder decodes the contents of b into v.
 | 
			
		||||
// It's primarily used for decoding contents of a file into a map[string]interface{}.
 | 
			
		||||
type Decoder interface {
 | 
			
		||||
	Decode(b []byte, v map[string]interface{}) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// ErrDecoderNotFound is returned when there is no decoder registered for a format.
 | 
			
		||||
	ErrDecoderNotFound = encodingError("decoder not found for this format")
 | 
			
		||||
 | 
			
		||||
	// ErrDecoderFormatAlreadyRegistered is returned when an decoder is already registered for a format.
 | 
			
		||||
	ErrDecoderFormatAlreadyRegistered = encodingError("decoder already registered for this format")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DecoderRegistry can choose an appropriate Decoder based on the provided format.
 | 
			
		||||
type DecoderRegistry struct {
 | 
			
		||||
	decoders map[string]Decoder
 | 
			
		||||
 | 
			
		||||
	mu sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDecoderRegistry returns a new, initialized DecoderRegistry.
 | 
			
		||||
func NewDecoderRegistry() *DecoderRegistry {
 | 
			
		||||
	return &DecoderRegistry{
 | 
			
		||||
		decoders: make(map[string]Decoder),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterDecoder registers a Decoder for a format.
 | 
			
		||||
// Registering a Decoder for an already existing format is not supported.
 | 
			
		||||
func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
 | 
			
		||||
	e.mu.Lock()
 | 
			
		||||
	defer e.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if _, ok := e.decoders[format]; ok {
 | 
			
		||||
		return ErrDecoderFormatAlreadyRegistered
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	e.decoders[format] = enc
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode calls the underlying Decoder based on the format.
 | 
			
		||||
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
 | 
			
		||||
	e.mu.RLock()
 | 
			
		||||
	decoder, ok := e.decoders[format]
 | 
			
		||||
	e.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return ErrDecoderNotFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return decoder.Decode(b, v)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								vendor/github.com/spf13/viper/internal/encoding/dotenv/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/spf13/viper/internal/encoding/dotenv/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
package dotenv
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/subosito/gotenv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const keyDelimiter = "_"
 | 
			
		||||
 | 
			
		||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for encoding data containing environment variables
 | 
			
		||||
// (commonly called as dotenv format).
 | 
			
		||||
type Codec struct{}
 | 
			
		||||
 | 
			
		||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	flattened := map[string]interface{}{}
 | 
			
		||||
 | 
			
		||||
	flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter)
 | 
			
		||||
 | 
			
		||||
	keys := make([]string, 0, len(flattened))
 | 
			
		||||
 | 
			
		||||
	for key := range flattened {
 | 
			
		||||
		keys = append(keys, key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Strings(keys)
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	for _, key := range keys {
 | 
			
		||||
		_, err := buf.WriteString(fmt.Sprintf("%v=%v\n", strings.ToUpper(key), flattened[key]))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	_, err := buf.Write(b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	env, err := gotenv.StrictParse(&buf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for key, value := range env {
 | 
			
		||||
		v[key] = value
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								vendor/github.com/spf13/viper/internal/encoding/dotenv/map_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/spf13/viper/internal/encoding/dotenv/map_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
package dotenv
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cast"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// flattenAndMergeMap recursively flattens the given map into a new map
 | 
			
		||||
// Code is based on the function with the same name in tha main package.
 | 
			
		||||
// TODO: move it to a common place
 | 
			
		||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
 | 
			
		||||
	if shadow != nil && prefix != "" && shadow[prefix] != nil {
 | 
			
		||||
		// prefix is shadowed => nothing more to flatten
 | 
			
		||||
		return shadow
 | 
			
		||||
	}
 | 
			
		||||
	if shadow == nil {
 | 
			
		||||
		shadow = make(map[string]interface{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var m2 map[string]interface{}
 | 
			
		||||
	if prefix != "" {
 | 
			
		||||
		prefix += delimiter
 | 
			
		||||
	}
 | 
			
		||||
	for k, val := range m {
 | 
			
		||||
		fullKey := prefix + k
 | 
			
		||||
		switch val.(type) {
 | 
			
		||||
		case map[string]interface{}:
 | 
			
		||||
			m2 = val.(map[string]interface{})
 | 
			
		||||
		case map[interface{}]interface{}:
 | 
			
		||||
			m2 = cast.ToStringMap(val)
 | 
			
		||||
		default:
 | 
			
		||||
			// immediate value
 | 
			
		||||
			shadow[strings.ToLower(fullKey)] = val
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// recursively merge to shadow map
 | 
			
		||||
		shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
 | 
			
		||||
	}
 | 
			
		||||
	return shadow
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								vendor/github.com/spf13/viper/internal/encoding/encoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/spf13/viper/internal/encoding/encoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Encoder encodes the contents of v into a byte representation.
 | 
			
		||||
// It's primarily used for encoding a map[string]interface{} into a file format.
 | 
			
		||||
type Encoder interface {
 | 
			
		||||
	Encode(v map[string]interface{}) ([]byte, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// ErrEncoderNotFound is returned when there is no encoder registered for a format.
 | 
			
		||||
	ErrEncoderNotFound = encodingError("encoder not found for this format")
 | 
			
		||||
 | 
			
		||||
	// ErrEncoderFormatAlreadyRegistered is returned when an encoder is already registered for a format.
 | 
			
		||||
	ErrEncoderFormatAlreadyRegistered = encodingError("encoder already registered for this format")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EncoderRegistry can choose an appropriate Encoder based on the provided format.
 | 
			
		||||
type EncoderRegistry struct {
 | 
			
		||||
	encoders map[string]Encoder
 | 
			
		||||
 | 
			
		||||
	mu sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewEncoderRegistry returns a new, initialized EncoderRegistry.
 | 
			
		||||
func NewEncoderRegistry() *EncoderRegistry {
 | 
			
		||||
	return &EncoderRegistry{
 | 
			
		||||
		encoders: make(map[string]Encoder),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterEncoder registers an Encoder for a format.
 | 
			
		||||
// Registering a Encoder for an already existing format is not supported.
 | 
			
		||||
func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error {
 | 
			
		||||
	e.mu.Lock()
 | 
			
		||||
	defer e.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if _, ok := e.encoders[format]; ok {
 | 
			
		||||
		return ErrEncoderFormatAlreadyRegistered
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	e.encoders[format] = enc
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *EncoderRegistry) Encode(format string, v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	e.mu.RLock()
 | 
			
		||||
	encoder, ok := e.encoders[format]
 | 
			
		||||
	e.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, ErrEncoderNotFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return encoder.Encode(v)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/github.com/spf13/viper/internal/encoding/error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/spf13/viper/internal/encoding/error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
type encodingError string
 | 
			
		||||
 | 
			
		||||
func (e encodingError) Error() string {
 | 
			
		||||
	return string(e)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								vendor/github.com/spf13/viper/internal/encoding/hcl/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/spf13/viper/internal/encoding/hcl/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
package hcl
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/hcl"
 | 
			
		||||
	"github.com/hashicorp/hcl/hcl/printer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for HCL encoding.
 | 
			
		||||
// TODO: add printer config to the codec?
 | 
			
		||||
type Codec struct{}
 | 
			
		||||
 | 
			
		||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	b, err := json.Marshal(v)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: use printer.Format? Is the trailing newline an issue?
 | 
			
		||||
 | 
			
		||||
	ast, err := hcl.Parse(string(b))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	err = printer.Fprint(&buf, ast.Node)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
 | 
			
		||||
	return hcl.Unmarshal(b, &v)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										99
									
								
								vendor/github.com/spf13/viper/internal/encoding/ini/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								vendor/github.com/spf13/viper/internal/encoding/ini/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
			
		||||
package ini
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cast"
 | 
			
		||||
	"gopkg.in/ini.v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LoadOptions contains all customized options used for load data source(s).
 | 
			
		||||
// This type is added here for convenience: this way consumers can import a single package called "ini".
 | 
			
		||||
type LoadOptions = ini.LoadOptions
 | 
			
		||||
 | 
			
		||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for INI encoding.
 | 
			
		||||
type Codec struct {
 | 
			
		||||
	KeyDelimiter string
 | 
			
		||||
	LoadOptions  LoadOptions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	cfg := ini.Empty()
 | 
			
		||||
	ini.PrettyFormat = false
 | 
			
		||||
 | 
			
		||||
	flattened := map[string]interface{}{}
 | 
			
		||||
 | 
			
		||||
	flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
 | 
			
		||||
 | 
			
		||||
	keys := make([]string, 0, len(flattened))
 | 
			
		||||
 | 
			
		||||
	for key := range flattened {
 | 
			
		||||
		keys = append(keys, key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Strings(keys)
 | 
			
		||||
 | 
			
		||||
	for _, key := range keys {
 | 
			
		||||
		sectionName, keyName := "", key
 | 
			
		||||
 | 
			
		||||
		lastSep := strings.LastIndex(key, ".")
 | 
			
		||||
		if lastSep != -1 {
 | 
			
		||||
			sectionName = key[:(lastSep)]
 | 
			
		||||
			keyName = key[(lastSep + 1):]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: is this a good idea?
 | 
			
		||||
		if sectionName == "default" {
 | 
			
		||||
			sectionName = ""
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cfg.Section(sectionName).Key(keyName).SetValue(cast.ToString(flattened[key]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	_, err := cfg.WriteTo(&buf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Codec) Decode(b []byte, v map[string]interface{}) error {
 | 
			
		||||
	cfg := ini.Empty(c.LoadOptions)
 | 
			
		||||
 | 
			
		||||
	err := cfg.Append(b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sections := cfg.Sections()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(sections); i++ {
 | 
			
		||||
		section := sections[i]
 | 
			
		||||
		keys := section.Keys()
 | 
			
		||||
 | 
			
		||||
		for j := 0; j < len(keys); j++ {
 | 
			
		||||
			key := keys[j]
 | 
			
		||||
			value := cfg.Section(section.Name()).Key(key.Name()).String()
 | 
			
		||||
 | 
			
		||||
			deepestMap := deepSearch(v, strings.Split(section.Name(), c.keyDelimiter()))
 | 
			
		||||
 | 
			
		||||
			// set innermost value
 | 
			
		||||
			deepestMap[key.Name()] = value
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Codec) keyDelimiter() string {
 | 
			
		||||
	if c.KeyDelimiter == "" {
 | 
			
		||||
		return "."
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.KeyDelimiter
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								vendor/github.com/spf13/viper/internal/encoding/ini/map_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								vendor/github.com/spf13/viper/internal/encoding/ini/map_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
			
		||||
package ini
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cast"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
 | 
			
		||||
// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE
 | 
			
		||||
// deepSearch scans deep maps, following the key indexes listed in the
 | 
			
		||||
// sequence "path".
 | 
			
		||||
// The last value is expected to be another map, and is returned.
 | 
			
		||||
//
 | 
			
		||||
// In case intermediate keys do not exist, or map to a non-map value,
 | 
			
		||||
// a new map is created and inserted, and the search continues from there:
 | 
			
		||||
// the initial map "m" may be modified!
 | 
			
		||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
 | 
			
		||||
	for _, k := range path {
 | 
			
		||||
		m2, ok := m[k]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			// intermediate key does not exist
 | 
			
		||||
			// => create it and continue from there
 | 
			
		||||
			m3 := make(map[string]interface{})
 | 
			
		||||
			m[k] = m3
 | 
			
		||||
			m = m3
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		m3, ok := m2.(map[string]interface{})
 | 
			
		||||
		if !ok {
 | 
			
		||||
			// intermediate key is a value
 | 
			
		||||
			// => replace with a new map
 | 
			
		||||
			m3 = make(map[string]interface{})
 | 
			
		||||
			m[k] = m3
 | 
			
		||||
		}
 | 
			
		||||
		// continue search from here
 | 
			
		||||
		m = m3
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// flattenAndMergeMap recursively flattens the given map into a new map
 | 
			
		||||
// Code is based on the function with the same name in tha main package.
 | 
			
		||||
// TODO: move it to a common place
 | 
			
		||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
 | 
			
		||||
	if shadow != nil && prefix != "" && shadow[prefix] != nil {
 | 
			
		||||
		// prefix is shadowed => nothing more to flatten
 | 
			
		||||
		return shadow
 | 
			
		||||
	}
 | 
			
		||||
	if shadow == nil {
 | 
			
		||||
		shadow = make(map[string]interface{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var m2 map[string]interface{}
 | 
			
		||||
	if prefix != "" {
 | 
			
		||||
		prefix += delimiter
 | 
			
		||||
	}
 | 
			
		||||
	for k, val := range m {
 | 
			
		||||
		fullKey := prefix + k
 | 
			
		||||
		switch val.(type) {
 | 
			
		||||
		case map[string]interface{}:
 | 
			
		||||
			m2 = val.(map[string]interface{})
 | 
			
		||||
		case map[interface{}]interface{}:
 | 
			
		||||
			m2 = cast.ToStringMap(val)
 | 
			
		||||
		default:
 | 
			
		||||
			// immediate value
 | 
			
		||||
			shadow[strings.ToLower(fullKey)] = val
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// recursively merge to shadow map
 | 
			
		||||
		shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
 | 
			
		||||
	}
 | 
			
		||||
	return shadow
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								vendor/github.com/spf13/viper/internal/encoding/javaproperties/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								vendor/github.com/spf13/viper/internal/encoding/javaproperties/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,86 @@
 | 
			
		||||
package javaproperties
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/magiconair/properties"
 | 
			
		||||
	"github.com/spf13/cast"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding.
 | 
			
		||||
type Codec struct {
 | 
			
		||||
	KeyDelimiter string
 | 
			
		||||
 | 
			
		||||
	// Store read properties on the object so that we can write back in order with comments.
 | 
			
		||||
	// This will only be used if the configuration read is a properties file.
 | 
			
		||||
	// TODO: drop this feature in v2
 | 
			
		||||
	// TODO: make use of the global properties object optional
 | 
			
		||||
	Properties *properties.Properties
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	if c.Properties == nil {
 | 
			
		||||
		c.Properties = properties.NewProperties()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	flattened := map[string]interface{}{}
 | 
			
		||||
 | 
			
		||||
	flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
 | 
			
		||||
 | 
			
		||||
	keys := make([]string, 0, len(flattened))
 | 
			
		||||
 | 
			
		||||
	for key := range flattened {
 | 
			
		||||
		keys = append(keys, key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Strings(keys)
 | 
			
		||||
 | 
			
		||||
	for _, key := range keys {
 | 
			
		||||
		_, _, err := c.Properties.Set(key, cast.ToString(flattened[key]))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	_, err := c.Properties.WriteComment(&buf, "#", properties.UTF8)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	c.Properties, err = properties.Load(b, properties.UTF8)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, key := range c.Properties.Keys() {
 | 
			
		||||
		// ignore existence check: we know it's there
 | 
			
		||||
		value, _ := c.Properties.Get(key)
 | 
			
		||||
 | 
			
		||||
		// recursively build nested maps
 | 
			
		||||
		path := strings.Split(key, c.keyDelimiter())
 | 
			
		||||
		lastKey := strings.ToLower(path[len(path)-1])
 | 
			
		||||
		deepestMap := deepSearch(v, path[0:len(path)-1])
 | 
			
		||||
 | 
			
		||||
		// set innermost value
 | 
			
		||||
		deepestMap[lastKey] = value
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Codec) keyDelimiter() string {
 | 
			
		||||
	if c.KeyDelimiter == "" {
 | 
			
		||||
		return "."
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.KeyDelimiter
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								vendor/github.com/spf13/viper/internal/encoding/javaproperties/map_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								vendor/github.com/spf13/viper/internal/encoding/javaproperties/map_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
			
		||||
package javaproperties
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cast"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
 | 
			
		||||
// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE
 | 
			
		||||
// deepSearch scans deep maps, following the key indexes listed in the
 | 
			
		||||
// sequence "path".
 | 
			
		||||
// The last value is expected to be another map, and is returned.
 | 
			
		||||
//
 | 
			
		||||
// In case intermediate keys do not exist, or map to a non-map value,
 | 
			
		||||
// a new map is created and inserted, and the search continues from there:
 | 
			
		||||
// the initial map "m" may be modified!
 | 
			
		||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
 | 
			
		||||
	for _, k := range path {
 | 
			
		||||
		m2, ok := m[k]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			// intermediate key does not exist
 | 
			
		||||
			// => create it and continue from there
 | 
			
		||||
			m3 := make(map[string]interface{})
 | 
			
		||||
			m[k] = m3
 | 
			
		||||
			m = m3
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		m3, ok := m2.(map[string]interface{})
 | 
			
		||||
		if !ok {
 | 
			
		||||
			// intermediate key is a value
 | 
			
		||||
			// => replace with a new map
 | 
			
		||||
			m3 = make(map[string]interface{})
 | 
			
		||||
			m[k] = m3
 | 
			
		||||
		}
 | 
			
		||||
		// continue search from here
 | 
			
		||||
		m = m3
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// flattenAndMergeMap recursively flattens the given map into a new map
 | 
			
		||||
// Code is based on the function with the same name in tha main package.
 | 
			
		||||
// TODO: move it to a common place
 | 
			
		||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
 | 
			
		||||
	if shadow != nil && prefix != "" && shadow[prefix] != nil {
 | 
			
		||||
		// prefix is shadowed => nothing more to flatten
 | 
			
		||||
		return shadow
 | 
			
		||||
	}
 | 
			
		||||
	if shadow == nil {
 | 
			
		||||
		shadow = make(map[string]interface{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var m2 map[string]interface{}
 | 
			
		||||
	if prefix != "" {
 | 
			
		||||
		prefix += delimiter
 | 
			
		||||
	}
 | 
			
		||||
	for k, val := range m {
 | 
			
		||||
		fullKey := prefix + k
 | 
			
		||||
		switch val.(type) {
 | 
			
		||||
		case map[string]interface{}:
 | 
			
		||||
			m2 = val.(map[string]interface{})
 | 
			
		||||
		case map[interface{}]interface{}:
 | 
			
		||||
			m2 = cast.ToStringMap(val)
 | 
			
		||||
		default:
 | 
			
		||||
			// immediate value
 | 
			
		||||
			shadow[strings.ToLower(fullKey)] = val
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// recursively merge to shadow map
 | 
			
		||||
		shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
 | 
			
		||||
	}
 | 
			
		||||
	return shadow
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/spf13/viper/internal/encoding/json/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/spf13/viper/internal/encoding/json/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
package json
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
 | 
			
		||||
type Codec struct{}
 | 
			
		||||
 | 
			
		||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	// TODO: expose prefix and indent in the Codec as setting?
 | 
			
		||||
	return json.MarshalIndent(v, "", "  ")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
 | 
			
		||||
	return json.Unmarshal(b, &v)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								vendor/github.com/spf13/viper/internal/encoding/toml/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/spf13/viper/internal/encoding/toml/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
//go:build viper_toml1
 | 
			
		||||
// +build viper_toml1
 | 
			
		||||
 | 
			
		||||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/pelletier/go-toml"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
 | 
			
		||||
type Codec struct{}
 | 
			
		||||
 | 
			
		||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	t, err := toml.TreeFromMap(v)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, err := t.ToTomlString()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return []byte(s), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
 | 
			
		||||
	tree, err := toml.LoadBytes(b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmap := tree.ToMap()
 | 
			
		||||
	for key, value := range tmap {
 | 
			
		||||
		v[key] = value
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								vendor/github.com/spf13/viper/internal/encoding/toml/codec2.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/spf13/viper/internal/encoding/toml/codec2.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
//go:build !viper_toml1
 | 
			
		||||
// +build !viper_toml1
 | 
			
		||||
 | 
			
		||||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/pelletier/go-toml/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
 | 
			
		||||
type Codec struct{}
 | 
			
		||||
 | 
			
		||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	return toml.Marshal(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
 | 
			
		||||
	return toml.Unmarshal(b, &v)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
package yaml
 | 
			
		||||
 | 
			
		||||
// import "gopkg.in/yaml.v2"
 | 
			
		||||
 | 
			
		||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
 | 
			
		||||
type Codec struct{}
 | 
			
		||||
 | 
			
		||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
 | 
			
		||||
	return yaml.Marshal(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
 | 
			
		||||
	return yaml.Unmarshal(b, &v)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								vendor/github.com/spf13/viper/internal/encoding/yaml/yaml2.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/spf13/viper/internal/encoding/yaml/yaml2.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
//go:build viper_yaml2
 | 
			
		||||
// +build viper_yaml2
 | 
			
		||||
 | 
			
		||||
package yaml
 | 
			
		||||
 | 
			
		||||
import yamlv2 "gopkg.in/yaml.v2"
 | 
			
		||||
 | 
			
		||||
var yaml = struct {
 | 
			
		||||
	Marshal   func(in interface{}) (out []byte, err error)
 | 
			
		||||
	Unmarshal func(in []byte, out interface{}) (err error)
 | 
			
		||||
}{
 | 
			
		||||
	Marshal:   yamlv2.Marshal,
 | 
			
		||||
	Unmarshal: yamlv2.Unmarshal,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								vendor/github.com/spf13/viper/internal/encoding/yaml/yaml3.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/spf13/viper/internal/encoding/yaml/yaml3.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
//go:build !viper_yaml2
 | 
			
		||||
// +build !viper_yaml2
 | 
			
		||||
 | 
			
		||||
package yaml
 | 
			
		||||
 | 
			
		||||
import yamlv3 "gopkg.in/yaml.v3"
 | 
			
		||||
 | 
			
		||||
var yaml = struct {
 | 
			
		||||
	Marshal   func(in interface{}) (out []byte, err error)
 | 
			
		||||
	Unmarshal func(in []byte, out interface{}) (err error)
 | 
			
		||||
}{
 | 
			
		||||
	Marshal:   yamlv3.Marshal,
 | 
			
		||||
	Unmarshal: yamlv3.Unmarshal,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								vendor/github.com/spf13/viper/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/spf13/viper/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,77 @@
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	jww "github.com/spf13/jwalterweatherman"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Logger is a unified interface for various logging use cases and practices, including:
 | 
			
		||||
//   - leveled logging
 | 
			
		||||
//   - structured logging
 | 
			
		||||
type Logger interface {
 | 
			
		||||
	// Trace logs a Trace event.
 | 
			
		||||
	//
 | 
			
		||||
	// Even more fine-grained information than Debug events.
 | 
			
		||||
	// Loggers not supporting this level should fall back to Debug.
 | 
			
		||||
	Trace(msg string, keyvals ...interface{})
 | 
			
		||||
 | 
			
		||||
	// Debug logs a Debug event.
 | 
			
		||||
	//
 | 
			
		||||
	// A verbose series of information events.
 | 
			
		||||
	// They are useful when debugging the system.
 | 
			
		||||
	Debug(msg string, keyvals ...interface{})
 | 
			
		||||
 | 
			
		||||
	// Info logs an Info event.
 | 
			
		||||
	//
 | 
			
		||||
	// General information about what's happening inside the system.
 | 
			
		||||
	Info(msg string, keyvals ...interface{})
 | 
			
		||||
 | 
			
		||||
	// Warn logs a Warn(ing) event.
 | 
			
		||||
	//
 | 
			
		||||
	// Non-critical events that should be looked at.
 | 
			
		||||
	Warn(msg string, keyvals ...interface{})
 | 
			
		||||
 | 
			
		||||
	// Error logs an Error event.
 | 
			
		||||
	//
 | 
			
		||||
	// Critical events that require immediate attention.
 | 
			
		||||
	// Loggers commonly provide Fatal and Panic levels above Error level,
 | 
			
		||||
	// but exiting and panicing is out of scope for a logging library.
 | 
			
		||||
	Error(msg string, keyvals ...interface{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type jwwLogger struct{}
 | 
			
		||||
 | 
			
		||||
func (jwwLogger) Trace(msg string, keyvals ...interface{}) {
 | 
			
		||||
	jww.TRACE.Printf(jwwLogMessage(msg, keyvals...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (jwwLogger) Debug(msg string, keyvals ...interface{}) {
 | 
			
		||||
	jww.DEBUG.Printf(jwwLogMessage(msg, keyvals...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (jwwLogger) Info(msg string, keyvals ...interface{}) {
 | 
			
		||||
	jww.INFO.Printf(jwwLogMessage(msg, keyvals...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (jwwLogger) Warn(msg string, keyvals ...interface{}) {
 | 
			
		||||
	jww.WARN.Printf(jwwLogMessage(msg, keyvals...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (jwwLogger) Error(msg string, keyvals ...interface{}) {
 | 
			
		||||
	jww.ERROR.Printf(jwwLogMessage(msg, keyvals...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func jwwLogMessage(msg string, keyvals ...interface{}) string {
 | 
			
		||||
	out := msg
 | 
			
		||||
 | 
			
		||||
	if len(keyvals) > 0 && len(keyvals)%2 == 1 {
 | 
			
		||||
		keyvals = append(keyvals, nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := 0; i <= len(keyvals)-2; i += 2 {
 | 
			
		||||
		out = fmt.Sprintf("%s %v=%v", out, keyvals[i], keyvals[i+1])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										217
									
								
								vendor/github.com/spf13/viper/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								vendor/github.com/spf13/viper/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,217 @@
 | 
			
		||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
 | 
			
		||||
//
 | 
			
		||||
// Use of this source code is governed by an MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Viper is a application configuration system.
 | 
			
		||||
// It believes that applications can be configured a variety of ways
 | 
			
		||||
// via flags, ENVIRONMENT variables, configuration files retrieved
 | 
			
		||||
// from the file system, or a remote key/value store.
 | 
			
		||||
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cast"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ConfigParseError denotes failing to parse configuration file.
 | 
			
		||||
type ConfigParseError struct {
 | 
			
		||||
	err error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error returns the formatted configuration error.
 | 
			
		||||
func (pe ConfigParseError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("While parsing config: %s", pe.err.Error())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// toCaseInsensitiveValue checks if the value is a  map;
 | 
			
		||||
// if so, create a copy and lower-case the keys recursively.
 | 
			
		||||
func toCaseInsensitiveValue(value interface{}) interface{} {
 | 
			
		||||
	switch v := value.(type) {
 | 
			
		||||
	case map[interface{}]interface{}:
 | 
			
		||||
		value = copyAndInsensitiviseMap(cast.ToStringMap(v))
 | 
			
		||||
	case map[string]interface{}:
 | 
			
		||||
		value = copyAndInsensitiviseMap(v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
 | 
			
		||||
// any map it makes case insensitive.
 | 
			
		||||
func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
 | 
			
		||||
	nm := make(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	for key, val := range m {
 | 
			
		||||
		lkey := strings.ToLower(key)
 | 
			
		||||
		switch v := val.(type) {
 | 
			
		||||
		case map[interface{}]interface{}:
 | 
			
		||||
			nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
 | 
			
		||||
		case map[string]interface{}:
 | 
			
		||||
			nm[lkey] = copyAndInsensitiviseMap(v)
 | 
			
		||||
		default:
 | 
			
		||||
			nm[lkey] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nm
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func insensitiviseVal(val interface{}) interface{} {
 | 
			
		||||
	switch val.(type) {
 | 
			
		||||
	case map[interface{}]interface{}:
 | 
			
		||||
		// nested map: cast and recursively insensitivise
 | 
			
		||||
		val = cast.ToStringMap(val)
 | 
			
		||||
		insensitiviseMap(val.(map[string]interface{}))
 | 
			
		||||
	case map[string]interface{}:
 | 
			
		||||
		// nested map: recursively insensitivise
 | 
			
		||||
		insensitiviseMap(val.(map[string]interface{}))
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		// nested array: recursively insensitivise
 | 
			
		||||
		insensitiveArray(val.([]interface{}))
 | 
			
		||||
	}
 | 
			
		||||
	return val
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func insensitiviseMap(m map[string]interface{}) {
 | 
			
		||||
	for key, val := range m {
 | 
			
		||||
		val = insensitiviseVal(val)
 | 
			
		||||
		lower := strings.ToLower(key)
 | 
			
		||||
		if key != lower {
 | 
			
		||||
			// remove old key (not lower-cased)
 | 
			
		||||
			delete(m, key)
 | 
			
		||||
		}
 | 
			
		||||
		// update map
 | 
			
		||||
		m[lower] = val
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func insensitiveArray(a []interface{}) {
 | 
			
		||||
	for i, val := range a {
 | 
			
		||||
		a[i] = insensitiviseVal(val)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func absPathify(logger Logger, inPath string) string {
 | 
			
		||||
	logger.Info("trying to resolve absolute path", "path", inPath)
 | 
			
		||||
 | 
			
		||||
	if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
 | 
			
		||||
		inPath = userHomeDir() + inPath[5:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inPath = os.ExpandEnv(inPath)
 | 
			
		||||
 | 
			
		||||
	if filepath.IsAbs(inPath) {
 | 
			
		||||
		return filepath.Clean(inPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p, err := filepath.Abs(inPath)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return filepath.Clean(p)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger.Error(fmt.Errorf("could not discover absolute path: %w", err).Error())
 | 
			
		||||
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func stringInSlice(a string, list []string) bool {
 | 
			
		||||
	for _, b := range list {
 | 
			
		||||
		if b == a {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func userHomeDir() string {
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
 | 
			
		||||
		if home == "" {
 | 
			
		||||
			home = os.Getenv("USERPROFILE")
 | 
			
		||||
		}
 | 
			
		||||
		return home
 | 
			
		||||
	}
 | 
			
		||||
	return os.Getenv("HOME")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func safeMul(a, b uint) uint {
 | 
			
		||||
	c := a * b
 | 
			
		||||
	if a > 1 && b > 1 && c/b != a {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
 | 
			
		||||
func parseSizeInBytes(sizeStr string) uint {
 | 
			
		||||
	sizeStr = strings.TrimSpace(sizeStr)
 | 
			
		||||
	lastChar := len(sizeStr) - 1
 | 
			
		||||
	multiplier := uint(1)
 | 
			
		||||
 | 
			
		||||
	if lastChar > 0 {
 | 
			
		||||
		if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
 | 
			
		||||
			if lastChar > 1 {
 | 
			
		||||
				switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
 | 
			
		||||
				case 'k':
 | 
			
		||||
					multiplier = 1 << 10
 | 
			
		||||
					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
 | 
			
		||||
				case 'm':
 | 
			
		||||
					multiplier = 1 << 20
 | 
			
		||||
					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
 | 
			
		||||
				case 'g':
 | 
			
		||||
					multiplier = 1 << 30
 | 
			
		||||
					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
 | 
			
		||||
				default:
 | 
			
		||||
					multiplier = 1
 | 
			
		||||
					sizeStr = strings.TrimSpace(sizeStr[:lastChar])
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	size := cast.ToInt(sizeStr)
 | 
			
		||||
	if size < 0 {
 | 
			
		||||
		size = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return safeMul(uint(size), multiplier)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// deepSearch scans deep maps, following the key indexes listed in the
 | 
			
		||||
// sequence "path".
 | 
			
		||||
// The last value is expected to be another map, and is returned.
 | 
			
		||||
//
 | 
			
		||||
// In case intermediate keys do not exist, or map to a non-map value,
 | 
			
		||||
// a new map is created and inserted, and the search continues from there:
 | 
			
		||||
// the initial map "m" may be modified!
 | 
			
		||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
 | 
			
		||||
	for _, k := range path {
 | 
			
		||||
		m2, ok := m[k]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			// intermediate key does not exist
 | 
			
		||||
			// => create it and continue from there
 | 
			
		||||
			m3 := make(map[string]interface{})
 | 
			
		||||
			m[k] = m3
 | 
			
		||||
			m = m3
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		m3, ok := m2.(map[string]interface{})
 | 
			
		||||
		if !ok {
 | 
			
		||||
			// intermediate key is a value
 | 
			
		||||
			// => replace with a new map
 | 
			
		||||
			m3 = make(map[string]interface{})
 | 
			
		||||
			m[k] = m3
 | 
			
		||||
		}
 | 
			
		||||
		// continue search from here
 | 
			
		||||
		m = m3
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2151
									
								
								vendor/github.com/spf13/viper/viper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2151
									
								
								vendor/github.com/spf13/viper/viper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										57
									
								
								vendor/github.com/spf13/viper/viper_go1_15.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/spf13/viper/viper_go1_15.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
//go:build !go1.16 || !finder
 | 
			
		||||
// +build !go1.16 !finder
 | 
			
		||||
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/afero"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Search all configPaths for any config file.
 | 
			
		||||
// Returns the first path that exists (and is a config file).
 | 
			
		||||
func (v *Viper) findConfigFile() (string, error) {
 | 
			
		||||
	v.logger.Info("searching for config in paths", "paths", v.configPaths)
 | 
			
		||||
 | 
			
		||||
	for _, cp := range v.configPaths {
 | 
			
		||||
		file := v.searchInPath(cp)
 | 
			
		||||
		if file != "" {
 | 
			
		||||
			return file, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Viper) searchInPath(in string) (filename string) {
 | 
			
		||||
	v.logger.Debug("searching for config in path", "path", in)
 | 
			
		||||
	for _, ext := range SupportedExts {
 | 
			
		||||
		v.logger.Debug("checking if file exists", "file", filepath.Join(in, v.configName+"."+ext))
 | 
			
		||||
		if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b {
 | 
			
		||||
			v.logger.Debug("found file", "file", filepath.Join(in, v.configName+"."+ext))
 | 
			
		||||
			return filepath.Join(in, v.configName+"."+ext)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.configType != "" {
 | 
			
		||||
		if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b {
 | 
			
		||||
			return filepath.Join(in, v.configName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check if file Exists
 | 
			
		||||
func exists(fs afero.Fs, path string) (bool, error) {
 | 
			
		||||
	stat, err := fs.Stat(path)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return !stat.IsDir(), nil
 | 
			
		||||
	}
 | 
			
		||||
	if os.IsNotExist(err) {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/spf13/viper/viper_go1_16.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/spf13/viper/viper_go1_16.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
//go:build go1.16 && finder
 | 
			
		||||
// +build go1.16,finder
 | 
			
		||||
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/afero"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Search all configPaths for any config file.
 | 
			
		||||
// Returns the first path that exists (and is a config file).
 | 
			
		||||
func (v *Viper) findConfigFile() (string, error) {
 | 
			
		||||
	finder := finder{
 | 
			
		||||
		paths:            v.configPaths,
 | 
			
		||||
		fileNames:        []string{v.configName},
 | 
			
		||||
		extensions:       SupportedExts,
 | 
			
		||||
		withoutExtension: v.configType != "",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	file, err := finder.Find(afero.NewIOFS(v.fs))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if file == "" {
 | 
			
		||||
		return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return file, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/spf13/viper/watch.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/spf13/viper/watch.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
//go:build !js
 | 
			
		||||
// +build !js
 | 
			
		||||
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
import "github.com/fsnotify/fsnotify"
 | 
			
		||||
 | 
			
		||||
type watcher = fsnotify.Watcher
 | 
			
		||||
 | 
			
		||||
func newWatcher() (*watcher, error) {
 | 
			
		||||
	return fsnotify.NewWatcher()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/spf13/viper/watch_wasm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/spf13/viper/watch_wasm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
// +build js,wasm
 | 
			
		||||
 | 
			
		||||
package viper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/fsnotify/fsnotify"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type watcher struct {
 | 
			
		||||
	Events chan fsnotify.Event
 | 
			
		||||
	Errors chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*watcher) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*watcher) Add(name string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*watcher) Remove(name string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newWatcher() (*watcher, error) {
 | 
			
		||||
	return &watcher{}, errors.New("fsnotify is not supported on WASM")
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user