Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Stuart a8d2f21f14
Fix nil pointer on watch function 2018-09-27 23:34:43 -07:00
71 changed files with 891 additions and 6169 deletions

View File

@ -1,15 +0,0 @@
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

View File

@ -1,2 +0,0 @@
[{*.yml,*.yaml}]
indent_size = 2

View File

@ -1,119 +0,0 @@
name: 🐛 Bug report
description: Report a bug to help us improve Viper
labels: [kind/bug]
body:
- type: markdown
attributes:
value: |
Thank you for submitting a bug report!
Please fill out the template below to make it easier to debug your problem.
If you are not sure if it is a bug or not, you can contact us via the available [support channels](https://git.internal/re/viper/issues/new/choose).
- type: checkboxes
attributes:
label: Preflight Checklist
description: Please ensure you've completed all of the following.
options:
- label: I have searched the [issue tracker](https://www.git.internal/re/viper/issues) for an issue that matches the one I want to file, without success.
required: true
- label: I am not looking for support or already pursued the available [support channels](https://git.internal/re/viper/issues/new/choose) without success.
required: true
- label: I have checked the [troubleshooting guide](https://git.internal/re/viper/blob/master/TROUBLESHOOTING.md) for my problem, without success.
required: true
- type: input
attributes:
label: Viper Version
description: What version of Viper are you using?
placeholder: 1.8.1
validations:
required: true
- type: input
attributes:
label: Go Version
description: What version of Go are you using?
placeholder: "1.16"
validations:
required: true
- type: dropdown
attributes:
label: Config Source
description: What sources do you load configuration from?
options:
- Manual set
- Flags
- Environment variables
- Files
- Remove K/V stores
- Defaults
multiple: true
validations:
required: true
- type: dropdown
attributes:
label: Format
description: Which file formats do you use?
options:
- JSON
- YAML
- TOML
- Dotenv
- HCL
- Java properties
- INI
- Other (specify below)
multiple: true
- type: input
attributes:
label: Repl.it link
description: Complete example on Repl.it reproducing the issue. [Here](https://repl.it/@sagikazarmark/Viper-example) is an example you can use.
placeholder: https://repl.it/@sagikazarmark/Viper-example
- type: textarea
attributes:
label: Code reproducing the issue
description: Please provide a Repl.it link if possible.
render: go
placeholder: |
package main
import (
"git.internal/re/viper"
)
func main() {
v := viper.New()
// ...
var config Config
err = v.Unmarshal(&config)
if err != nil {
panic(err)
}
}
- type: textarea
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Actual Behavior
description: A clear description of what actually happens.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior if it is not self-explanatory.
placeholder: |
1. In this environment...
2. With this config...
3. Run '...'
4. See error...
- type: textarea
attributes:
label: Additional Information
description: Links? References? Anything that will give us more context about the issue that you are encountering!

View File

@ -1,13 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: ❓ Ask a question
url: https://git.internal/re/viper/discussions/new?category=q-a
about: Ask and discuss questions with other Viper community members
- name: 📓 Reference
url: https://pkg.go.dev/mod/git.internal/re/viper
about: Check the Go code reference
- name: 💬 Slack channel
url: https://gophers.slack.com/messages/viper
about: Please ask and answer questions here

View File

@ -1,39 +0,0 @@
name: 🎉 Feature request
description: Suggest an idea for Viper
labels: [kind/enhancement]
body:
- type: markdown
attributes:
value: |
Thank you for submitting a feature request!
Please describe what you would like to change/add and why in detail by filling out the template below.
If you are not sure if your request fits into Viper, you can contact us via the available [support channels](https://git.internal/re/viper/issues/new/choose).
- type: checkboxes
attributes:
label: Preflight Checklist
description: Please ensure you've completed all of the following.
options:
- label: I have searched the [issue tracker](https://www.git.internal/re/viper/issues) for an issue that matches the one I want to file, without success.
required: true
- type: textarea
attributes:
label: Problem Description
description: A clear and concise description of the problem you are seeking to solve with this feature request.
validations:
required: true
- type: textarea
attributes:
label: Proposed Solution
description: A clear and concise description of what would you like to happen.
validations:
required: true
- type: textarea
attributes:
label: Alternatives Considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
attributes:
label: Additional Information
description: Add any other context about the problem here.

View File

@ -1,20 +0,0 @@
<!--
Thank you for sending a pull request! Here some tips for contributors:
1. Fill the description template below.
2. Include appropriate tests (if necessary). Make sure that all CI checks passed.
3. If the Pull Request is a work in progress, make use of GitHub's "Draft PR" feature and mark it as such.
-->
**Overview**:
<!-- Describe your changes briefly here. -->
**What problem does it solve?**:
<!--
- Please state in detail why we need this PR and what it solves.
- If your PR closes some of the existing issues, please add links to them here.
Mentioned issues will be automatically closed.
Usage: "Closes #<issue number>", or "Closes (paste link of issue)"
-->
**Special notes for a reviewer**:

View File

@ -1,16 +0,0 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
labels:
- area/dependencies
schedule:
interval: daily
- package-ecosystem: github-actions
directory: /
labels:
- area/dependencies
schedule:
interval: daily

BIN
.github/logo.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

30
.github/release.yml vendored
View File

@ -1,30 +0,0 @@
changelog:
exclude:
labels:
- release-note/ignore
categories:
- title: Exciting New Features 🎉
labels:
- kind/feature
- release-note/new-feature
- title: Enhancements 🚀
labels:
- kind/enhancement
- release-note/enhancement
- title: Bug Fixes 🐛
labels:
- kind/bug
- release-note/bug-fix
- title: Breaking Changes 🛠
labels:
- release-note/breaking-change
- title: Deprecations ❌
labels:
- release-note/deprecation
- title: Dependency Updates ⬆️
labels:
- area/dependencies
- release-note/dependency-update
- title: Other Changes
labels:
- "*"

View File

@ -1,18 +0,0 @@
name: PR Checks
on:
pull_request:
types: [opened, labeled, unlabeled, synchronize]
jobs:
release-label:
name: Release note label
runs-on: ubuntu-latest
steps:
- name: Check minimum labels
uses: mheap/github-action-required-labels@v2
with:
mode: minimum
count: 1
labels: "release-note/ignore, kind/feature, release-note/new-feature, kind/enhancement, release-note/enhancement, kind/bug, release-note/bug-fix, release-note/breaking-change, release-note/deprecation, area/dependencies, release-note/dependency-update"

View File

@ -1,86 +0,0 @@
name: CI
on:
push:
branches:
- master
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- goos: js
goarch: wasm
- goos: aix
goarch: ppc64
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Checkout code
uses: actions/checkout@v3
- name: Build
run: go build .
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
# Fail fast is disabled because there are Go version specific features and tests
# that should be able to fail independently.
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: ['1.16', '1.17', '1.18', '1.19']
tags: ['', 'viper_yaml2', 'viper_toml1']
env:
GOFLAGS: -mod=readonly
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Checkout code
uses: actions/checkout@v3
- name: Test
run: go test -race -tags '${{ matrix.tags }}' -v ./...
if: runner.os != 'Windows'
- name: Test (without race detector)
run: go test -tags '${{ matrix.tags }}' -v ./...
if: runner.os == 'Windows'
lint:
name: Lint
runs-on: ubuntu-latest
env:
GOFLAGS: -mod=readonly
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Checkout code
uses: actions/checkout@v3
- name: Lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.49

View File

@ -1,72 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '22 16 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@ -1,32 +0,0 @@
on:
issues:
types: [opened]
jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `👋 Thanks for reporting!
A maintainer will take a look at your issue shortly. 👀
In the meantime: We are working on **Viper v2** and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.
⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9
📣 If you've already given us your feedback, you can still help by spreading the news,
either by sharing the above link or telling people about this on Twitter:
https://twitter.com/sagikazarmark/status/1306904078967074816
**Thank you!** ❤️
`,
})

View File

@ -1,32 +0,0 @@
on:
pull_request_target:
types: [opened]
jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `👋 Thanks for contributing to Viper! You are awesome! 🎉
A maintainer will take a look at your pull request shortly. 👀
In the meantime: We are working on **Viper v2** and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.
⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9
📣 If you've already given us your feedback, you can still help by spreading the news,
either by sharing the above link or telling people about this on Twitter:
https://twitter.com/sagikazarmark/status/1306904078967074816
**Thank you!** ❤️
`,
})

29
.gitignore vendored
View File

@ -1,5 +1,24 @@
/.idea/ # Compiled Object files, Static and Dynamic libs (Shared Objects)
/bin/ *.o
/build/ *.a
/var/ *.so
/vendor/
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.bench

View File

@ -1,96 +0,0 @@
run:
timeout: 5m
linters-settings:
gci:
sections:
- standard
- default
- prefix(git.internal/re/viper)
golint:
min-confidence: 0
goimports:
local-prefixes: git.internal/re/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

27
.travis.yml Normal file
View File

@ -0,0 +1,27 @@
go_import_path: github.com/spf13/viper
language: go
go:
- 1.10.x
- 1.11.x
- tip
os:
- linux
- osx
matrix:
allow_failures:
- go: tip
fast_finish: true
script:
- go install ./...
- diff -u <(echo -n) <(gofmt -d .)
- go test -v ./...
after_success:
- go get -u -d github.com/spf13/hugo
- cd $GOPATH/src/github.com/spf13/hugo && make && ./hugo -s docs && cd -
sudo: false

View File

@ -1,76 +0,0 @@
# 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 $*=$($*)

379
README.md
View File

@ -1,20 +1,6 @@
> ## Viper v2 feedback ![viper logo](https://cloud.githubusercontent.com/assets/173412/10886745/998df88a-8151-11e5-9448-4736db51020d.png)
> 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!**
![Viper](.github/logo.png?raw=true) Go configuration with fangs!
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
[![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://git.internal/re/viper/actions?query=workflow%3ACI)
[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Go Report Card](https://goreportcard.com/badge/git.internal/re/viper?style=flat-square)](https://goreportcard.com/report/git.internal/re/viper)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square)
[![PkgGoDev](https://pkg.go.dev/badge/mod/git.internal/re/viper)](https://pkg.go.dev/mod/git.internal/re/viper)
**Go configuration with fangs!**
Many Go projects are built using Viper including: Many Go projects are built using Viper including:
@ -26,16 +12,8 @@ Many Go projects are built using Viper including:
* [BloomApi](https://www.bloomapi.com/) * [BloomApi](https://www.bloomapi.com/)
* [doctl](https://github.com/digitalocean/doctl) * [doctl](https://github.com/digitalocean/doctl)
* [Clairctl](https://github.com/jgsqware/clairctl) * [Clairctl](https://github.com/jgsqware/clairctl)
* [Mercure](https://mercure.rocks)
[![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper)
## Install
```shell
go get git.internal/re/viper
```
**Note:** Viper uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies.
## What is Viper? ## What is Viper?
@ -45,7 +23,7 @@ to work within an application, and can handle all types of configuration needs
and formats. It supports: and formats. It supports:
* setting defaults * setting defaults
* reading from JSON, TOML, YAML, HCL, envfile and Java properties config files * reading from JSON, TOML, YAML, HCL, and Java properties config files
* live watching and re-reading of config files (optional) * live watching and re-reading of config files (optional)
* reading from environment variables * reading from environment variables
* reading from remote config systems (etcd or Consul), and watching changes * reading from remote config systems (etcd or Consul), and watching changes
@ -53,8 +31,8 @@ and formats. It supports:
* reading from buffer * reading from buffer
* setting explicit values * setting explicit values
Viper can be thought of as a registry for all of your applications configuration needs. Viper can be thought of as a registry for all of your applications
configuration needs.
## Why Viper? ## Why Viper?
@ -64,31 +42,34 @@ Viper is here to help with that.
Viper does the following for you: 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. 1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, or Java properties formats.
2. Provide a mechanism to set default values for your different configuration options. 2. Provide a mechanism to set default values for your different
3. Provide a mechanism to set override values for options specified through command line flags. configuration options.
4. Provide an alias system to easily rename parameters without breaking existing code. 3. Provide a mechanism to set override values for options specified through
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. 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: Viper uses the following precedence order. Each item takes precedence over the
item below it:
* explicit call to `Set` * explicit call to Set
* flag * flag
* env * env
* config * config
* key/value store * key/value store
* default * default
**Important:** Viper configuration keys are case insensitive. Viper configuration keys are case insensitive.
There are ongoing discussions about making that optional.
## Putting Values into Viper ## Putting Values into Viper
### Establishing Defaults ### Establishing Defaults
A good configuration system will support default values. A default value is not A good configuration system will support default values. A default value is not
required for a key, but its useful in the event that a key hasn't been set via required for a key, but its useful in the event that a key hasnt been set via
config file, environment variable, remote configuration or flag. config file, environment variable, remote configuration or flag.
Examples: Examples:
@ -102,7 +83,7 @@ viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "cat
### Reading Config Files ### Reading Config Files
Viper requires minimal configuration so it knows where to look for 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 Viper supports JSON, TOML, YAML, HCL, and Java Properties files. Viper can search multiple paths, but
currently a single Viper instance only supports a single configuration file. currently a single Viper instance only supports a single configuration file.
Viper does not default to any configuration search paths leaving defaults decision Viper does not default to any configuration search paths leaving defaults decision
to an application. to an application.
@ -113,54 +94,15 @@ where a configuration file is expected.
```go ```go
viper.SetConfigName("config") // name of config file (without extension) 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("/etc/appname/") // path to look for the config file in
viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths
viper.AddConfigPath(".") // optionally look for config in the working directory viper.AddConfigPath(".") // optionally look for config in the working directory
err := viper.ReadInConfig() // Find and read the config file err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file if err != nil { // Handle errors reading the config file
panic(fmt.Errorf("fatal error config file: %w", err)) panic(fmt.Errorf("Fatal error config file: %s \n", 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 ### Watching and re-reading config files
Viper supports the ability to have your application live read a config file while running. Viper supports the ability to have your application live read a config file while running.
@ -175,10 +117,10 @@ 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()`** **Make sure you add all of the configPaths prior to calling `WatchConfig()`**
```go ```go
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) { viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name) fmt.Println("Config file changed:", e.Name)
}) })
viper.WatchConfig()
``` ```
### Reading Config from io.Reader ### Reading Config from io.Reader
@ -237,14 +179,13 @@ viper.GetBool("verbose") // true
### Working with Environment Variables ### Working with Environment Variables
Viper has full support for environment variables. This enables 12 factor 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 applications out of the box. There are four methods that exist to aid working
with ENV: with ENV:
* `AutomaticEnv()` * `AutomaticEnv()`
* `BindEnv(string...) : error` * `BindEnv(string...) : error`
* `SetEnvPrefix(string)` * `SetEnvPrefix(string)`
* `SetEnvKeyReplacer(string...) *strings.Replacer` * `SetEnvKeyReplacer(string...) *strings.Replacer`
* `AllowEmptyEnv(bool)`
_When working with ENV variables, its important to recognize that Viper _When working with ENV variables, its important to recognize that Viper
treats ENV variables as case sensitive._ treats ENV variables as case sensitive._
@ -254,13 +195,12 @@ using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from
the environment variables. Both `BindEnv` and `AutomaticEnv` will use this the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
prefix. prefix.
`BindEnv` takes one or more parameters. The first parameter is the key name, the `BindEnv` takes one or two 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 second is the name of the environment variable. The name of the environment
one are provided, they will take precedence in the specified order. The name of variable is case sensitive. If the ENV variable name is not provided, then
the environment variable is case sensitive. If the ENV variable name is not provided, then Viper will automatically assume that the key name matches the ENV variable name,
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), but the ENV variable is IN ALL CAPS. When you explicitly provide the ENV
it **does not** automatically add the prefix. For example if the second parameter is "id", variable name, it **does not** automatically add the prefix.
Viper will look for the ENV variable "ID".
One important thing to recognize when working with ENV variables is that the 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 value will be read each time it is accessed. Viper does not fix the value when
@ -269,7 +209,7 @@ the `BindEnv` is called.
`AutomaticEnv` is a powerful helper especially when combined with `AutomaticEnv` is a powerful helper especially when combined with
`SetEnvPrefix`. When called, Viper will check for an environment variable any `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 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 check for a environment variable with a name matching the key uppercased and
prefixed with the `EnvPrefix` if set. prefixed with the `EnvPrefix` if set.
`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env `SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env
@ -277,13 +217,6 @@ 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 `Get()` calls, but want your environmental variables to use `_` delimiters. An
example of using it can be found in `viper_test.go`. 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 #### Env example
```go ```go
@ -354,7 +287,7 @@ func main() {
i := viper.GetInt("flagname") // retrieve value from viper i := viper.GetInt("flagname") // retrieve value from viper
// ... ...
} }
``` ```
@ -406,14 +339,14 @@ viper.BindFlagValues("my-flags", fSet)
To enable remote support in Viper, do a blank import of the `viper/remote` To enable remote support in Viper, do a blank import of the `viper/remote`
package: package:
`import _ "git.internal/re/viper/remote"` `import _ "github.com/spf13/viper/remote"`
Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path Viper will read a config string (as JSON, TOML, YAML or HCL) retrieved from a path
in a Key/Value store such as etcd or Consul. These values take precedence over 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, default values, but are overridden by configuration values retrieved from disk,
flags, or environment variables. flags, or environment variables.
Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve Viper uses [crypt](https://github.com/xordataexchange/crypt) to retrieve
configuration from the K/V store, which means that you can store your configuration from the K/V store, which means that you can store your
configuration values encrypted and have them automatically decrypted if you have configuration values encrypted and have them automatically decrypted if you have
the correct gpg keyring. Encryption is optional. the correct gpg keyring. Encryption is optional.
@ -425,7 +358,7 @@ independently of it.
K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001. K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001.
```bash ```bash
$ go get github.com/bketelsen/crypt/bin/crypt $ go get github.com/xordataexchange/crypt/bin/crypt
$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
``` ```
@ -443,14 +376,7 @@ how to use Consul.
#### etcd #### etcd
```go ```go
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json") 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" viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
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() err := viper.ReadRemoteConfig()
``` ```
@ -474,21 +400,11 @@ fmt.Println(viper.Get("port")) // 8080
fmt.Println(viper.Get("hostname")) // myhostname.com 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 ### Remote Key/Value Store Example - Encrypted
```go ```go
viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg") 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" viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
err := viper.ReadRemoteConfig() err := viper.ReadRemoteConfig()
``` ```
@ -499,7 +415,7 @@ err := viper.ReadRemoteConfig()
var runtime_viper = viper.New() var runtime_viper = viper.New()
runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml") 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" 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"
// read from remote config the first time. // read from remote config the first time.
err := runtime_viper.ReadRemoteConfig() err := runtime_viper.ReadRemoteConfig()
@ -510,18 +426,18 @@ runtime_viper.Unmarshal(&runtime_conf)
// open a goroutine to watch remote changes forever // open a goroutine to watch remote changes forever
go func(){ go func(){
for { for {
time.Sleep(time.Second * 5) // delay after each request time.Sleep(time.Second * 5) // delay after each request
// currently, only tested with etcd support // currently, only tested with etcd support
err := runtime_viper.WatchRemoteConfig() err := runtime_viper.WatchRemoteConfig()
if err != nil { if err != nil {
log.Errorf("unable to read remote config: %v", err) log.Errorf("unable to read remote config: %v", err)
continue continue
} }
// unmarshal new config into our runtime config struct. you can also use channel // unmarshal new config into our runtime config struct. you can also use channel
// to implement a signal to notify the system of the changes // to implement a signal to notify the system of the changes
runtime_viper.Unmarshal(&runtime_conf) runtime_viper.Unmarshal(&runtime_conf)
} }
}() }()
``` ```
@ -535,7 +451,6 @@ The following functions and methods exist:
* `GetBool(key string) : bool` * `GetBool(key string) : bool`
* `GetFloat64(key string) : float64` * `GetFloat64(key string) : float64`
* `GetInt(key string) : int` * `GetInt(key string) : int`
* `GetIntSlice(key string) : []int`
* `GetString(key string) : string` * `GetString(key string) : string`
* `GetStringMap(key string) : map[string]interface{}` * `GetStringMap(key string) : map[string]interface{}`
* `GetStringMapString(key string) : map[string]string` * `GetStringMapString(key string) : map[string]string`
@ -553,7 +468,7 @@ Example:
```go ```go
viper.GetString("logfile") // case-insensitive Setting & Getting viper.GetString("logfile") // case-insensitive Setting & Getting
if viper.GetBool("verbose") { if viper.GetBool("verbose") {
fmt.Println("verbose enabled") fmt.Println("verbose enabled")
} }
``` ```
### Accessing nested keys ### Accessing nested keys
@ -599,37 +514,10 @@ the `Set()` method, …) with an immediate value, then all sub-keys of
`datastore.metric` become undefined, they are “shadowed” by the higher-priority `datastore.metric` become undefined, they are “shadowed” by the higher-priority
configuration level. 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 Lastly, if there exists a key that matches the delimited key path, its value
will be returned instead. E.g. will be returned instead. E.g.
```jsonc ```json
{ {
"datastore.metric.host": "0.0.0.0", "datastore.metric.host": "0.0.0.0",
"host": { "host": {
@ -651,15 +539,14 @@ will be returned instead. E.g.
GetString("datastore.metric.host") // returns "0.0.0.0" GetString("datastore.metric.host") // returns "0.0.0.0"
``` ```
### Extracting a sub-tree ### Extract sub-tree
When developing reusable modules, it's often useful to extract a subset of the configuration Extract sub-tree from Viper.
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: For example, `viper` represents:
```yaml ```json
cache: app:
cache1: cache1:
max-items: 100 max-items: 100
item-size: 64 item-size: 64
@ -668,36 +555,35 @@ cache:
item-size: 80 item-size: 80
``` ```
We could pass the cache name to a module (eg. `NewCache("cache1")`), After executing:
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 ```go
cache1Config := viper.Sub("cache.cache1") subv := viper.Sub("app.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. `subv` represents:
Internally, the `NewCache` function can address `max-items` and `item-size` keys directly: ```json
max-items: 100
item-size: 64
```
Suppose we have:
```go ```go
func NewCache(v *Viper) *Cache { func NewCache(cfg *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, which creates a cache based on config information formatted as `subv`.
and easier to reuse (for the same reason). Now its easy to create these 2 caches separately as:
```go
cfg1 := viper.Sub("app.cache1")
cache1 := NewCache(cfg1)
cfg2 := viper.Sub("app.cache2")
cache2 := NewCache(cfg2)
```
### Unmarshaling ### Unmarshaling
@ -720,97 +606,29 @@ type config struct {
var C config var C config
err := viper.Unmarshal(&C) err := Unmarshal(&C)
if err != nil { if err != nil {
t.Fatalf("unable to decode into struct, %v", err) 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 ### 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 may need to marhsal 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()`. You can use your favorite format's marshaller with the config returned by `AllSettings()`.
```go ```go
import ( import (
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
// ... // ...
) )
func yamlStringSettings() string { func yamlStringSettings() string {
c := viper.AllSettings() c := viper.AllSettings()
bs, err := yaml.Marshal(c) bs, err := yaml.Marshal(c)
if err != nil { if err != nil {
log.Fatalf("unable to marshal config to YAML: %v", err) t.Fatalf("unable to marshal config to YAML: %v", err)
} }
return string(bs) return string(bs)
} }
``` ```
@ -847,35 +665,22 @@ y.SetDefault("ContentDir", "foobar")
When working with multiple vipers, it is up to the user to keep track of the When working with multiple vipers, it is up to the user to keep track of the
different vipers. different vipers.
## Q & A ## Q & A
### Why is it called “Viper”? Q: Why not INI files?
A: Ini files are pretty awful. Theres no standard format, and they are hard to
validate. Viper is designed to work with JSON, TOML or YAML files. If someone
really wants to add this feature, Id be happy to merge it. Its easy to specify
which formats your application will permit.
Q: Why is it called “Viper”?
A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe)) 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 to [Cobra](https://github.com/spf13/cobra). While both can operate completely
independently, together they make a powerful pair to handle much of your independently, together they make a powerful pair to handle much of your
application foundation needs. application foundation needs.
### Why is it called “Cobra”? Q: Why is it called “Cobra”?
Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)? A: 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://git.internal/re/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).

View File

@ -1,32 +0,0 @@
# 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)

View File

@ -1,11 +0,0 @@
//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
})
}

View File

@ -36,7 +36,7 @@ type pflagValue struct {
flag *pflag.Flag flag *pflag.Flag
} }
// HasChanged returns whether the flag has changes or not. // HasChanges returns whether the flag has changes or not.
func (p pflagValue) HasChanged() bool { func (p pflagValue) HasChanged() bool {
return p.flag.Changed return p.flag.Changed
} }

View File

@ -10,13 +10,13 @@ import (
func TestBindFlagValueSet(t *testing.T) { func TestBindFlagValueSet(t *testing.T) {
flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
testValues := map[string]*string{ var testValues = map[string]*string{
"host": nil, "host": nil,
"port": nil, "port": nil,
"endpoint": nil, "endpoint": nil,
} }
mutatedTestValues := map[string]string{ var mutatedTestValues = map[string]string{
"host": "localhost", "host": "localhost",
"port": "6060", "port": "6060",
"endpoint": "/public", "endpoint": "/public",
@ -44,8 +44,8 @@ func TestBindFlagValueSet(t *testing.T) {
} }
func TestBindFlagValue(t *testing.T) { func TestBindFlagValue(t *testing.T) {
testString := "testing" var testString = "testing"
testValue := newStringValue(testString, &testString) var testValue = newStringValue(testString, &testString)
flag := &pflag.Flag{ flag := &pflag.Flag{
Name: "testflag", Name: "testflag",
@ -59,7 +59,7 @@ func TestBindFlagValue(t *testing.T) {
assert.Equal(t, testString, Get("testvalue")) assert.Equal(t, testString, Get("testvalue"))
flag.Value.Set("testing_mutate") flag.Value.Set("testing_mutate")
flag.Changed = true // hack for pflag usage flag.Changed = true //hack for pflag usage
assert.Equal(t, "testing_mutate", Get("testvalue")) assert.Equal(t, "testing_mutate", Get("testvalue"))
} }

65
fs.go
View File

@ -1,65 +0,0 @@
//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
}

View File

@ -1,100 +0,0 @@
//go:build go1.16 && finder
// +build go1.16,finder
package viper
import (
"io/fs"
"testing"
"testing/fstest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFinder(t *testing.T) {
t.Parallel()
fsys := fstest.MapFS{
"home/user/.config": &fstest.MapFile{},
"home/user/config.json": &fstest.MapFile{},
"home/user/config.yaml": &fstest.MapFile{},
"home/user/data.json": &fstest.MapFile{},
"etc/config/.config": &fstest.MapFile{},
"etc/config/a_random_file.txt": &fstest.MapFile{},
"etc/config/config.json": &fstest.MapFile{},
"etc/config/config.yaml": &fstest.MapFile{},
"etc/config/config.xml": &fstest.MapFile{},
}
testCases := []struct {
name string
fsys func() fs.FS
finder finder
result string
}{
{
name: "find file",
fsys: func() fs.FS { return fsys },
finder: finder{
paths: []string{"etc/config"},
fileNames: []string{"config"},
extensions: []string{"json"},
},
result: "etc/config/config.json",
},
{
name: "file not found",
fsys: func() fs.FS { return fsys },
finder: finder{
paths: []string{"var/config"},
fileNames: []string{"config"},
extensions: []string{"json"},
},
result: "",
},
{
name: "empty search params",
fsys: func() fs.FS { return fsys },
finder: finder{},
result: "",
},
{
name: "precedence",
fsys: func() fs.FS { return fsys },
finder: finder{
paths: []string{"var/config", "home/user", "etc/config"},
fileNames: []string{"aconfig", "config"},
extensions: []string{"zml", "xml", "json"},
},
result: "home/user/config.json",
},
{
name: "without extension",
fsys: func() fs.FS { return fsys },
finder: finder{
paths: []string{"var/config", "home/user", "etc/config"},
fileNames: []string{".config"},
extensions: []string{"zml", "xml", "json"},
withoutExtension: true,
},
result: "home/user/.config",
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
fsys := testCase.fsys()
result, err := testCase.finder.Find(fsys)
require.NoError(t, err)
assert.Equal(t, testCase.result, result)
})
}
}

39
go.mod
View File

@ -1,29 +1,16 @@
module git.internal/re/viper module github.com/spf13/viper
go 1.17
require ( require (
git.internal/re/afero v1.9.3 github.com/fsnotify/fsnotify v1.4.7
git.internal/re/cast v1.5.1 github.com/hashicorp/hcl v1.0.0
github.com/fsnotify/fsnotify v1.6.0 github.com/magiconair/properties v1.8.0
github.com/magiconair/properties v1.8.7 github.com/mitchellh/mapstructure v1.0.0
github.com/mitchellh/mapstructure v1.5.0 github.com/pelletier/go-toml v1.2.0
github.com/pelletier/go-toml v1.9.5 github.com/spf13/afero v1.1.2
github.com/pelletier/go-toml/v2 v2.0.5 github.com/spf13/cast v1.2.0
github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/jwalterweatherman v1.0.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.2
github.com/stretchr/testify v1.8.1 golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 // indirect
github.com/subosito/gotenv v1.4.1 golang.org/x/text v0.3.0 // indirect
gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v2 v2.2.1
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect
golang.org/x/text v0.4.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
) )

515
go.sum
View File

@ -1,495 +1,26 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.internal/re/afero v1.9.3 h1:3E6gTxdfID9str9lOLWuOQu+QILArbRLUBWi8MywP18=
git.internal/re/afero v1.9.3/go.mod h1:uyj2gu0urns+v+G7KIkNO49qShF4zekJMCflZVvB84M=
git.internal/re/cast v1.5.1 h1:lmg2A9oeAp9CZjvC70Bcv2hOuQO5RB027bIbixTbok4=
git.internal/re/cast v1.5.1/go.mod h1:ydbf5sDush+h+PruReXSzPmL7F0nRL1ogRYehzUSHnE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 h1:BH3eQWeGbwRU2+wxxuuPOdFBmaiBH81O8BugSjHeTFg=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@ -1,61 +0,0 @@
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)
}

View File

@ -1,81 +0,0 @@
package encoding
import (
"reflect"
"testing"
)
type decoder struct {
v map[string]interface{}
}
func (d decoder) Decode(_ []byte, v map[string]interface{}) error {
for key, value := range d.v {
v[key] = value
}
return nil
}
func TestDecoderRegistry_RegisterDecoder(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewDecoderRegistry()
err := registry.RegisterDecoder("myformat", decoder{})
if err != nil {
t.Fatal(err)
}
})
t.Run("AlreadyRegistered", func(t *testing.T) {
registry := NewDecoderRegistry()
err := registry.RegisterDecoder("myformat", decoder{})
if err != nil {
t.Fatal(err)
}
err = registry.RegisterDecoder("myformat", decoder{})
if err != ErrDecoderFormatAlreadyRegistered {
t.Fatalf("expected ErrDecoderFormatAlreadyRegistered, got: %v", err)
}
})
}
func TestDecoderRegistry_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewDecoderRegistry()
decoder := decoder{
v: map[string]interface{}{
"key": "value",
},
}
err := registry.RegisterDecoder("myformat", decoder)
if err != nil {
t.Fatal(err)
}
v := map[string]interface{}{}
err = registry.Decode("myformat", []byte("key: value"), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(decoder.v, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %+v\nexpected: %+v", v, decoder.v)
}
})
t.Run("DecoderNotFound", func(t *testing.T) {
registry := NewDecoderRegistry()
v := map[string]interface{}{}
err := registry.Decode("myformat", nil, v)
if err != ErrDecoderNotFound {
t.Fatalf("expected ErrDecoderNotFound, got: %v", err)
}
})
}

View File

@ -1,61 +0,0 @@
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
}

View File

@ -1,63 +0,0 @@
package dotenv
import (
"reflect"
"testing"
)
// original form of the data
const original = `# key-value pair
KEY=value
`
// encoded form of the data
const encoded = `KEY=value
`
// Viper's internal representation
var data = map[string]interface{}{
"KEY": "value",
}
func TestCodec_Encode(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
})
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err)
})
}

View File

@ -1,41 +0,0 @@
package dotenv
import (
"strings"
"git.internal/re/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
}

View File

@ -1,60 +0,0 @@
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)
}

View File

@ -1,70 +0,0 @@
package encoding
import (
"testing"
)
type encoder struct {
b []byte
}
func (e encoder) Encode(_ map[string]interface{}) ([]byte, error) {
return e.b, nil
}
func TestEncoderRegistry_RegisterEncoder(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewEncoderRegistry()
err := registry.RegisterEncoder("myformat", encoder{})
if err != nil {
t.Fatal(err)
}
})
t.Run("AlreadyRegistered", func(t *testing.T) {
registry := NewEncoderRegistry()
err := registry.RegisterEncoder("myformat", encoder{})
if err != nil {
t.Fatal(err)
}
err = registry.RegisterEncoder("myformat", encoder{})
if err != ErrEncoderFormatAlreadyRegistered {
t.Fatalf("expected ErrEncoderFormatAlreadyRegistered, got: %v", err)
}
})
}
func TestEncoderRegistry_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewEncoderRegistry()
encoder := encoder{
b: []byte("key: value"),
}
err := registry.RegisterEncoder("myformat", encoder)
if err != nil {
t.Fatal(err)
}
b, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
if err != nil {
t.Fatal(err)
}
if string(b) != "key: value" {
t.Fatalf("expected 'key: value', got: %#v", string(b))
}
})
t.Run("EncoderNotFound", func(t *testing.T) {
registry := NewEncoderRegistry()
_, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
if err != ErrEncoderNotFound {
t.Fatalf("expected ErrEncoderNotFound, got: %v", err)
}
})
}

View File

@ -1,7 +0,0 @@
package encoding
type encodingError string
func (e encodingError) Error() string {
return string(e)
}

View File

@ -1,37 +0,0 @@
package hcl
import (
"bytes"
)
// 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)
return nil
}

View File

@ -1,140 +0,0 @@
package hcl
import (
"reflect"
"testing"
)
// original form of the data
const original = `# key-value pair
"key" = "value"
// list
"list" = ["item1", "item2", "item3"]
/* map */
"map" = {
"key" = "value"
}
/*
nested map
*/
"nested_map" "map" {
"key" = "value"
"list" = ["item1", "item2", "item3"]
}`
// encoded form of the data
const encoded = `"key" = "value"
"list" = ["item1", "item2", "item3"]
"map" = {
"key" = "value"
}
"nested_map" "map" {
"key" = "value"
"list" = ["item1", "item2", "item3"]
}`
// decoded form of the data
//
// in case of HCL it's slightly different from Viper's internal representation
// (eg. map is decoded into a list of maps)
var decoded = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": []map[string]interface{}{
{
"key": "value",
},
},
"nested_map": []map[string]interface{}{
{
"map": []map[string]interface{}{
{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
},
},
}
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
func TestCodec_Encode(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(decoded, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
}
})
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err)
})
}

View File

@ -1,99 +0,0 @@
package ini
import (
"bytes"
"sort"
"strings"
"git.internal/re/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
}

View File

@ -1,111 +0,0 @@
package ini
import (
"reflect"
"testing"
)
// original form of the data
const original = `; key-value pair
key=value ; key-value pair
# map
[map] # map
key=%(key)s
`
// encoded form of the data
const encoded = `key=value
[map]
key=value
`
// decoded form of the data
//
// in case of INI it's slightly different from Viper's internal representation
// (eg. top level keys land in a section called default)
var decoded = map[string]interface{}{
"DEFAULT": map[string]interface{}{
"key": "value",
},
"map": map[string]interface{}{
"key": "value",
},
}
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"map": map[string]interface{}{
"key": "value",
},
}
func TestCodec_Encode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
})
t.Run("Default", func(t *testing.T) {
codec := Codec{}
data := map[string]interface{}{
"default": map[string]interface{}{
"key": "value",
},
"map": map[string]interface{}{
"key": "value",
},
}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
})
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(decoded, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
}
})
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err)
})
}

View File

@ -1,74 +0,0 @@
package ini
import (
"strings"
"git.internal/re/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
}

View File

@ -1,86 +0,0 @@
package javaproperties
import (
"bytes"
"sort"
"strings"
"git.internal/re/cast"
"github.com/magiconair/properties"
)
// 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
}

View File

@ -1,89 +0,0 @@
package javaproperties
import (
"reflect"
"testing"
)
// original form of the data
const original = `#key-value pair
key = value
map.key = value
`
// encoded form of the data
const encoded = `key = value
map.key = value
`
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"map": map[string]interface{}{
"key": "value",
},
}
func TestCodec_Encode(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
})
t.Run("InvalidData", func(t *testing.T) {
t.Skip("TODO: needs invalid data example")
codec := Codec{}
v := map[string]interface{}{}
codec.Decode([]byte(``), v)
if len(v) > 0 {
t.Fatalf("expected map to be empty when data is invalid\nactual: %#v", v)
}
})
}
func TestCodec_DecodeEncode(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if original != string(b) {
t.Fatalf("encoded value does not match the original\nactual: %#v\nexpected: %#v", string(b), original)
}
}

View File

@ -1,74 +0,0 @@
package javaproperties
import (
"strings"
"git.internal/re/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
}

View File

@ -1,17 +0,0 @@
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)
}

View File

@ -1,95 +0,0 @@
package json
import (
"reflect"
"testing"
)
// encoded form of the data
const encoded = `{
"key": "value",
"list": [
"item1",
"item2",
"item3"
],
"map": {
"key": "value"
},
"nested_map": {
"map": {
"key": "value",
"list": [
"item1",
"item2",
"item3"
]
}
}
}`
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
func TestCodec_Encode(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(encoded), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
})
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err)
})
}

View File

@ -1,39 +0,0 @@
//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
}

View File

@ -1,19 +0,0 @@
//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)
}

View File

@ -1,108 +0,0 @@
//go:build !viper_toml1
// +build !viper_toml1
package toml
import (
"reflect"
"testing"
)
// original form of the data
const original = `# key-value pair
key = "value"
list = ["item1", "item2", "item3"]
[map]
key = "value"
# nested
# map
[nested_map]
[nested_map.map]
key = "value"
list = [
"item1",
"item2",
"item3",
]
`
// encoded form of the data
const encoded = `key = 'value'
list = ['item1', 'item2', 'item3']
[map]
key = 'value'
[nested_map]
[nested_map.map]
key = 'value'
list = ['item1', 'item2', 'item3']
`
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
func TestCodec_Encode(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
})
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err)
})
}

View File

@ -1,109 +0,0 @@
//go:build viper_toml1
// +build viper_toml1
package toml
import (
"reflect"
"testing"
)
// original form of the data
const original = `# key-value pair
key = "value"
list = ["item1", "item2", "item3"]
[map]
key = "value"
# nested
# map
[nested_map]
[nested_map.map]
key = "value"
list = [
"item1",
"item2",
"item3",
]
`
// encoded form of the data
const encoded = `key = "value"
list = ["item1", "item2", "item3"]
[map]
key = "value"
[nested_map]
[nested_map.map]
key = "value"
list = ["item1", "item2", "item3"]
`
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
func TestCodec_Encode(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(data, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
}
})
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err)
})
}

View File

@ -1,14 +0,0 @@
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)
}

View File

@ -1,49 +0,0 @@
package yaml
import (
"reflect"
"testing"
)
func TestCodec_Encode(t *testing.T) {
codec := Codec{}
b, err := codec.Encode(data)
if err != nil {
t.Fatal(err)
}
if encoded != string(b) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
}
}
func TestCodec_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(original), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(decoded, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
}
})
t.Run("InvalidData", func(t *testing.T) {
codec := Codec{}
v := map[string]interface{}{}
err := codec.Decode([]byte(`invalid data`), v)
if err == nil {
t.Fatal("expected decoding to fail")
}
t.Logf("decoding failed as expected: %s", err)
})
}

View File

@ -1,14 +0,0 @@
//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,
}

View File

@ -1,91 +0,0 @@
//go:build viper_yaml2
// +build viper_yaml2
package yaml
// original form of the data
const original = `# key-value pair
key: value
list:
- item1
- item2
- item3
map:
key: value
# nested
# map
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// encoded form of the data
const encoded = `key: value
list:
- item1
- item2
- item3
map:
key: value
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// decoded form of the data
//
// in case of YAML it's slightly different from Viper's internal representation
// (eg. map is decoded into a map with interface key)
var decoded = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[interface{}]interface{}{
"key": "value",
},
"nested_map": map[interface{}]interface{}{
"map": map[interface{}]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}

View File

@ -1,14 +0,0 @@
//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,
}

View File

@ -1,91 +0,0 @@
//go:build !viper_yaml2
// +build !viper_yaml2
package yaml
// original form of the data
const original = `# key-value pair
key: value
list:
- item1
- item2
- item3
map:
key: value
# nested
# map
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// encoded form of the data
const encoded = `key: value
list:
- item1
- item2
- item3
map:
key: value
nested_map:
map:
key: value
list:
- item1
- item2
- item3
`
// decoded form of the data
//
// in case of YAML it's slightly different from Viper's internal representation
// (eg. map is decoded into a map with interface key)
var decoded = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}
// Viper's internal representation
var data = map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"map": map[string]interface{}{
"key": "value",
},
"nested_map": map[string]interface{}{
"map": map[string]interface{}{
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
},
},
}

View File

@ -1,40 +0,0 @@
//go:build !go1.17
// +build !go1.17
package testutil
import (
"os"
"testing"
)
// Based on https://github.com/frankban/quicktest/blob/577841610793d24f99e31cc2c0ef3a541fefd7c7/patch.go#L34-L64
// Licensed under the MIT license
// Copyright (c) 2017 Canonical Ltd.
// Setenv sets an environment variable to a temporary value for the
// duration of the test.
//
// At the end of the test (see "Deferred execution" in the package docs), the
// environment variable is returned to its original value.
func Setenv(t *testing.T, name, val string) {
setenv(t, name, val, true)
}
// setenv sets or unsets an environment variable to a temporary value for the
// duration of the test
func setenv(t *testing.T, name, val string, valOK bool) {
oldVal, oldOK := os.LookupEnv(name)
if valOK {
os.Setenv(name, val)
} else {
os.Unsetenv(name)
}
t.Cleanup(func() {
if oldOK {
os.Setenv(name, oldVal)
} else {
os.Unsetenv(name)
}
})
}

View File

@ -1,18 +0,0 @@
//go:build go1.17
// +build go1.17
package testutil
import (
"testing"
)
// Setenv sets an environment variable to a temporary value for the
// duration of the test.
//
// This shim can be removed once support for Go <1.17 is dropped.
func Setenv(t *testing.T, name, val string) {
t.Helper()
t.Setenv(name, val)
}

View File

@ -1,18 +0,0 @@
package testutil
import (
"path/filepath"
"testing"
)
// AbsFilePath calls filepath.Abs on path.
func AbsFilePath(t *testing.T, path string) string {
t.Helper()
s, err := filepath.Abs(path)
if err != nil {
t.Fatal(err)
}
return s
}

View File

@ -1,77 +0,0 @@
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
}

View File

@ -5,7 +5,7 @@ import (
"strings" "strings"
"testing" "testing"
"git.internal/re/cast" "github.com/spf13/cast"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -78,7 +78,6 @@ func TestNestedOverrides(t *testing.T) {
func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper { func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue) return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)
} }
func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper { func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue) return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
} }

View File

@ -6,110 +6,100 @@
// Package remote integrates the remote features of Viper. // Package remote integrates the remote features of Viper.
package remote package remote
// import ( import (
// "bytes" "bytes"
// "io" "io"
// "os" "os"
// crypt "github.com/sagikazarmark/crypt/config" "github.com/spf13/viper"
crypt "github.com/xordataexchange/crypt/config"
)
// "git.internal/re/viper" type remoteConfigProvider struct{}
// )
// type remoteConfigProvider struct{} func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
cm, err := getConfigManager(rp)
if err != nil {
return nil, err
}
b, err := cm.Get(rp.Path())
if err != nil {
return nil, err
}
return bytes.NewReader(b), nil
}
// func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) { func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
// cm, err := getConfigManager(rp) cm, err := getConfigManager(rp)
// if err != nil { if err != nil {
// return nil, err return nil, err
// } }
// b, err := cm.Get(rp.Path()) resp, err := cm.Get(rp.Path())
// if err != nil { if err != nil {
// return nil, err return nil, err
// } }
// return bytes.NewReader(b), nil
// }
// func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) { return bytes.NewReader(resp), nil
// cm, err := getConfigManager(rp) }
// if err != nil {
// return nil, err
// }
// resp, err := cm.Get(rp.Path())
// if err != nil {
// return nil, err
// }
// return bytes.NewReader(resp), nil func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
// } cm, err := getConfigManager(rp)
if err != nil {
return nil, nil
}
quit := make(chan bool)
quitwc := make(chan bool)
viperResponsCh := make(chan *viper.RemoteResponse)
cryptoResponseCh := cm.Watch(rp.Path(), quit)
// need this function to convert the Channel response form crypt.Response to viper.Response
go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
for {
select {
case <-quitwc:
quit <- true
return
case resp := <-cr:
vr <- &viper.RemoteResponse{
Error: resp.Error,
Value: resp.Value,
}
// func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) { }
// cm, err := getConfigManager(rp)
// if err != nil {
// return nil, nil
// }
// quit := make(chan bool)
// quitwc := make(chan bool)
// viperResponsCh := make(chan *viper.RemoteResponse)
// cryptoResponseCh := cm.Watch(rp.Path(), quit)
// // need this function to convert the Channel response form crypt.Response to viper.Response
// go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
// for {
// select {
// case <-quitwc:
// quit <- true
// return
// case resp := <-cr:
// vr <- &viper.RemoteResponse{
// Error: resp.Error,
// Value: resp.Value,
// }
// }
// }
// }(cryptoResponseCh, viperResponsCh, quitwc, quit)
// return viperResponsCh, quitwc }
// } }(cryptoResponseCh, viperResponsCh, quitwc, quit)
// func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) { return viperResponsCh, quitwc
// var cm crypt.ConfigManager }
// var err error
// if rp.SecretKeyring() != "" { func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
// var kr *os.File var cm crypt.ConfigManager
// kr, err = os.Open(rp.SecretKeyring()) var err error
// if err != nil {
// return nil, err
// }
// defer kr.Close()
// switch rp.Provider() {
// case "etcd":
// cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr)
// case "etcd3":
// cm, err = crypt.NewEtcdV3ConfigManager([]string{rp.Endpoint()}, kr)
// case "firestore":
// cm, err = crypt.NewFirestoreConfigManager([]string{rp.Endpoint()}, kr)
// default:
// cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr)
// }
// } else {
// switch rp.Provider() {
// case "etcd":
// cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()})
// case "etcd3":
// cm, err = crypt.NewStandardEtcdV3ConfigManager([]string{rp.Endpoint()})
// case "firestore":
// cm, err = crypt.NewStandardFirestoreConfigManager([]string{rp.Endpoint()})
// default:
// cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()})
// }
// }
// if err != nil {
// return nil, err
// }
// return cm, nil
// }
// func init() { if rp.SecretKeyring() != "" {
// viper.RemoteConfig = &remoteConfigProvider{} kr, err := os.Open(rp.SecretKeyring())
// } defer kr.Close()
if err != nil {
return nil, err
}
if rp.Provider() == "etcd" {
cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr)
} else {
cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr)
}
} else {
if rp.Provider() == "etcd" {
cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()})
} else {
cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()})
}
}
if err != nil {
return nil, err
}
return cm, nil
}
func init() {
viper.RemoteConfig = &remoteConfigProvider{}
}

64
util.go
View File

@ -18,7 +18,9 @@ import (
"strings" "strings"
"unicode" "unicode"
"git.internal/re/cast" "github.com/spf13/afero"
"github.com/spf13/cast"
jww "github.com/spf13/jwalterweatherman"
) )
// ConfigParseError denotes failing to parse configuration file. // ConfigParseError denotes failing to parse configuration file.
@ -64,25 +66,18 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
return nm 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{}) { func insensitiviseMap(m map[string]interface{}) {
for key, val := range m { for key, val := range m {
val = insensitiviseVal(val) 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{}))
}
lower := strings.ToLower(key) lower := strings.ToLower(key)
if key != lower { if key != lower {
// remove old key (not lower-cased) // remove old key (not lower-cased)
@ -93,20 +88,17 @@ func insensitiviseMap(m map[string]interface{}) {
} }
} }
func insensitiveArray(a []interface{}) { func absPathify(inPath string) string {
for i, val := range a { jww.INFO.Println("Trying to resolve absolute path to", inPath)
a[i] = insensitiviseVal(val)
}
}
func absPathify(logger Logger, inPath string) string { if strings.HasPrefix(inPath, "$HOME") {
logger.Info("trying to resolve absolute path", "path", inPath)
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
inPath = userHomeDir() + inPath[5:] inPath = userHomeDir() + inPath[5:]
} }
inPath = os.ExpandEnv(inPath) if strings.HasPrefix(inPath, "$") {
end := strings.Index(inPath, string(os.PathSeparator))
inPath = os.Getenv(inPath[1:end]) + inPath[end:]
}
if filepath.IsAbs(inPath) { if filepath.IsAbs(inPath) {
return filepath.Clean(inPath) return filepath.Clean(inPath)
@ -117,11 +109,23 @@ func absPathify(logger Logger, inPath string) string {
return filepath.Clean(p) return filepath.Clean(p)
} }
logger.Error(fmt.Errorf("could not discover absolute path: %w", err).Error()) jww.ERROR.Println("Couldn't discover absolute path")
jww.ERROR.Println(err)
return "" return ""
} }
// Check if File / Directory Exists
func exists(fs afero.Fs, path string) (bool, error) {
_, err := fs.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
func stringInSlice(a string, list []string) bool { func stringInSlice(a string, list []string) bool {
for _, b := range list { for _, b := range list {
if b == a { if b == a {

View File

@ -11,29 +11,25 @@
package viper package viper
import ( import (
"os"
"path/filepath"
"reflect" "reflect"
"testing" "testing"
"git.internal/re/viper/internal/testutil"
) )
func TestCopyAndInsensitiviseMap(t *testing.T) { func TestCopyAndInsensitiviseMap(t *testing.T) {
var ( var (
given = map[string]interface{}{ given = map[string]interface{}{
"Foo": 32, "Foo": 32,
"Bar": map[interface{}]interface{}{ "Bar": map[interface{}]interface {
}{
"ABc": "A", "ABc": "A",
"cDE": "B", "cDE": "B"},
},
} }
expected = map[string]interface{}{ expected = map[string]interface{}{
"foo": 32, "foo": 32,
"bar": map[string]interface{}{ "bar": map[string]interface {
}{
"abc": "A", "abc": "A",
"cde": "B", "cde": "B"},
},
} }
) )
@ -56,40 +52,3 @@ func TestCopyAndInsensitiviseMap(t *testing.T) {
t.Fatal("Input map changed") t.Fatal("Input map changed")
} }
} }
func TestAbsPathify(t *testing.T) {
skipWindows(t)
home := userHomeDir()
homer := filepath.Join(home, "homer")
wd, _ := os.Getwd()
testutil.Setenv(t, "HOMER_ABSOLUTE_PATH", homer)
testutil.Setenv(t, "VAR_WITH_RELATIVE_PATH", "relative")
tests := []struct {
input string
output string
}{
{"", wd},
{"sub", filepath.Join(wd, "sub")},
{"./", wd},
{"./sub", filepath.Join(wd, "sub")},
{"$HOME", home},
{"$HOME/", home},
{"$HOME/sub", filepath.Join(home, "sub")},
{"$HOMER_ABSOLUTE_PATH", homer},
{"$HOMER_ABSOLUTE_PATH/", homer},
{"$HOMER_ABSOLUTE_PATH/sub", filepath.Join(homer, "sub")},
{"$VAR_WITH_RELATIVE_PATH", filepath.Join(wd, "relative")},
{"$VAR_WITH_RELATIVE_PATH/", filepath.Join(wd, "relative")},
{"$VAR_WITH_RELATIVE_PATH/sub", filepath.Join(wd, "relative", "sub")},
}
for _, test := range tests {
got := absPathify(jwwLogger{}, test.input)
if got != test.output {
t.Errorf("Got %v\nexpected\n%q", got, test.output)
}
}
}

1091
viper.go

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +0,0 @@
//go:build !go1.16 || !finder
// +build !go1.16 !finder
package viper
import (
"fmt"
"os"
"path/filepath"
"git.internal/re/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
}

View File

@ -1,32 +0,0 @@
//go:build go1.16 && finder
// +build go1.16,finder
package viper
import (
"fmt"
"git.internal/re/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
}

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +0,0 @@
//go:build viper_yaml2
// +build viper_yaml2
package viper
var yamlExample = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
jacket: leather
trousers: denim
pants:
size: large
age: 35
eyes : brown
beard: true
`)
var yamlWriteExpected = []byte(`age: 35
beard: true
clothing:
jacket: leather
pants:
size: large
trousers: denim
eyes: brown
hacker: true
hobbies:
- skateboarding
- snowboarding
- go
name: steve
`)
var yamlExampleWithDot = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
jacket: leather
trousers: denim
pants:
size: large
age: 35
eyes : brown
beard: true
emails:
steve@hacker.com:
created: 01/02/03
active: true
`)

View File

@ -1,56 +0,0 @@
//go:build !viper_yaml2
// +build !viper_yaml2
package viper
var yamlExample = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
jacket: leather
trousers: denim
pants:
size: large
age: 35
eyes : brown
beard: true
`)
var yamlWriteExpected = []byte(`age: 35
beard: true
clothing:
jacket: leather
pants:
size: large
trousers: denim
eyes: brown
hacker: true
hobbies:
- skateboarding
- snowboarding
- go
name: steve
`)
var yamlExampleWithDot = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
jacket: leather
trousers: denim
pants:
size: large
age: 35
eyes : brown
beard: true
emails:
steve@hacker.com:
created: 01/02/03
active: true
`)

View File

@ -1,12 +0,0 @@
//go:build darwin || dragonfly || freebsd || openbsd || linux || netbsd || solaris || windows
// +build darwin dragonfly freebsd openbsd linux netbsd solaris windows
package viper
import "github.com/fsnotify/fsnotify"
type watcher = fsnotify.Watcher
func newWatcher() (*watcher, error) {
return fsnotify.NewWatcher()
}

View File

@ -1,32 +0,0 @@
//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
package viper
import (
"fmt"
"runtime"
"github.com/fsnotify/fsnotify"
)
func newWatcher() (*watcher, error) {
return &watcher{}, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
}
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
}