Upgrade to go 1.19 and update dependencies (#3069)

* Update to go 1.19
* Update dependencies
* Update cross-compile script
* Add missing targets to cross-compile-all
* Update cache action to remove warning
This commit is contained in:
WithoutPants
2022-11-04 13:41:26 +11:00
committed by GitHub
parent f25881a3bf
commit bba7c23957
939 changed files with 101336 additions and 43819 deletions

View File

@@ -1,26 +0,0 @@
sudo: false
language: go
arch:
- amd64
- ppc64e
go:
- "1.14"
- "1.15"
- "1.16"
- tip
os:
- linux
- osx
matrix:
allow_failures:
- go: tip
fast_finish: true
script:
- go build -v ./...
- go test -count=1 -cover -race -v ./...
- go vet ./...
- FILES=$(gofmt -s -l . zipfs sftpfs mem tarfs); if [[ -n "${FILES}" ]]; then echo "You have go format errors; gofmt your changes"; exit 1; fi

View File

@@ -2,7 +2,7 @@
A FileSystem Abstraction System for Go
[![Build Status](https://travis-ci.org/spf13/afero.svg)](https://travis-ci.org/spf13/afero) [![Build status](https://ci.appveyor.com/api/projects/status/github/spf13/afero?branch=master&svg=true)](https://ci.appveyor.com/project/spf13/afero) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Test](https://github.com/spf13/afero/actions/workflows/test.yml/badge.svg)](https://github.com/spf13/afero/actions/workflows/test.yml) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
# Overview

View File

@@ -103,8 +103,8 @@ type Fs interface {
var (
ErrFileClosed = errors.New("File is closed")
ErrOutOfRange = errors.New("Out of range")
ErrTooLarge = errors.New("Too large")
ErrOutOfRange = errors.New("out of range")
ErrTooLarge = errors.New("too large")
ErrFileNotFound = os.ErrNotExist
ErrFileExists = os.ErrExist
ErrDestinationExists = os.ErrExist

View File

@@ -1,3 +1,5 @@
# This currently does nothing. We have moved to GitHub action, but this is kept
# until spf13 has disabled this project in AppVeyor.
version: '{build}'
clone_folder: C:\gopath\src\github.com\spf13\afero
environment:
@@ -6,10 +8,3 @@ build_script:
- cmd: >-
go version
go env
go get -v github.com/spf13/afero/...
go build -v github.com/spf13/afero/...
test_script:
- cmd: go test -count=1 -cover -race -v github.com/spf13/afero/...

View File

@@ -1,6 +1,7 @@
package afero
import (
"io/fs"
"os"
"path/filepath"
"runtime"
@@ -8,7 +9,10 @@ import (
"time"
)
var _ Lstater = (*BasePathFs)(nil)
var (
_ Lstater = (*BasePathFs)(nil)
_ fs.ReadDirFile = (*BasePathFile)(nil)
)
// The BasePathFs restricts all operations to a given path within an Fs.
// The given file name to the operations on this Fs will be prepended with
@@ -33,6 +37,14 @@ func (f *BasePathFile) Name() string {
return strings.TrimPrefix(sourcename, filepath.Clean(f.path))
}
func (f *BasePathFile) ReadDir(n int) ([]fs.DirEntry, error) {
if rdf, ok := f.File.(fs.ReadDirFile); ok {
return rdf.ReadDir(n)
}
return readDirFile{f.File}.ReadDir(n)
}
func NewBasePathFs(source Fs, path string) Fs {
return &BasePathFs{source: source, path: path}
}

View File

@@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build aix || darwin || openbsd || freebsd || netbsd || dragonfly
// +build aix darwin openbsd freebsd netbsd dragonfly
package afero

View File

@@ -10,12 +10,8 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !darwin
// +build !openbsd
// +build !freebsd
// +build !dragonfly
// +build !netbsd
// +build !aix
//go:build !darwin && !openbsd && !freebsd && !dragonfly && !netbsd && !aix
// +build !darwin,!openbsd,!freebsd,!dragonfly,!netbsd,!aix
package afero

View File

@@ -29,7 +29,7 @@ type httpDir struct {
}
func (d httpDir) Open(name string) (http.File, error) {
if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) ||
strings.Contains(name, "\x00") {
return nil, errors.New("http: invalid character in file path")
}

View File

@@ -0,0 +1,27 @@
// Copyright © 2022 Steve Francia <spf@spf13.com>.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import "io/fs"
// FileInfoDirEntry provides an adapter from os.FileInfo to fs.DirEntry
type FileInfoDirEntry struct {
fs.FileInfo
}
var _ fs.DirEntry = FileInfoDirEntry{}
func (d FileInfoDirEntry) Type() fs.FileMode { return d.FileInfo.Mode().Type() }
func (d FileInfoDirEntry) Info() (fs.FileInfo, error) { return d.FileInfo, nil }

View File

@@ -1,3 +1,4 @@
//go:build go1.16
// +build go1.16
package afero
@@ -7,7 +8,10 @@ import (
"io/fs"
"os"
"path"
"sort"
"time"
"github.com/spf13/afero/internal/common"
)
// IOFS adopts afero.Fs to stdlib io/fs.FS
@@ -66,14 +70,31 @@ func (iofs IOFS) Glob(pattern string) ([]string, error) {
}
func (iofs IOFS) ReadDir(name string) ([]fs.DirEntry, error) {
items, err := ReadDir(iofs.Fs, name)
f, err := iofs.Fs.Open(name)
if err != nil {
return nil, iofs.wrapError("readdir", name, err)
}
defer f.Close()
if rdf, ok := f.(fs.ReadDirFile); ok {
items, err := rdf.ReadDir(-1)
if err != nil {
return nil, iofs.wrapError("readdir", name, err)
}
sort.Slice(items, func(i, j int) bool { return items[i].Name() < items[j].Name() })
return items, nil
}
items, err := f.Readdir(-1)
if err != nil {
return nil, iofs.wrapError("readdir", name, err)
}
sort.Sort(byName(items))
ret := make([]fs.DirEntry, len(items))
for i := range items {
ret[i] = dirEntry{items[i]}
ret[i] = common.FileInfoDirEntry{FileInfo: items[i]}
}
return ret, nil
@@ -108,17 +129,6 @@ func (IOFS) wrapError(op, path string, err error) error {
}
}
// dirEntry provides adapter from os.FileInfo to fs.DirEntry
type dirEntry struct {
fs.FileInfo
}
var _ fs.DirEntry = dirEntry{}
func (d dirEntry) Type() fs.FileMode { return d.FileInfo.Mode().Type() }
func (d dirEntry) Info() (fs.FileInfo, error) { return d.FileInfo, nil }
// readDirFile provides adapter from afero.File to fs.ReadDirFile needed for correct Open
type readDirFile struct {
File
@@ -134,7 +144,7 @@ func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) {
ret := make([]fs.DirEntry, len(items))
for i := range items {
ret[i] = dirEntry{items[i]}
ret[i] = common.FileInfoDirEntry{FileInfo: items[i]}
}
return ret, nil

View File

@@ -141,7 +141,7 @@ func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error {
// We generate random temporary file names so that there's a good
// chance the file doesn't exist yet - keeps the number of tries in
// TempFile to a minimum.
var rand uint32
var randNum uint32
var randmu sync.Mutex
func reseed() uint32 {
@@ -150,12 +150,12 @@ func reseed() uint32 {
func nextRandom() string {
randmu.Lock()
r := rand
r := randNum
if r == 0 {
r = reseed()
}
r = r*1664525 + 1013904223 // constants from Numerical Recipes
rand = r
randNum = r
randmu.Unlock()
return strconv.Itoa(int(1e9 + r%1e9))[1:]
}
@@ -194,7 +194,7 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) {
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
randmu.Lock()
rand = reseed()
randNum = reseed()
randmu.Unlock()
}
continue
@@ -226,7 +226,7 @@ func TempDir(fs Fs, dir, prefix string) (name string, err error) {
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
randmu.Lock()
rand = reseed()
randNum = reseed()
randmu.Unlock()
}
continue

View File

@@ -18,15 +18,20 @@ import (
"bytes"
"errors"
"io"
"io/fs"
"os"
"path/filepath"
"sync"
"sync/atomic"
"time"
"github.com/spf13/afero/internal/common"
)
const FilePathSeparator = string(filepath.Separator)
var _ fs.ReadDirFile = &File{}
type File struct {
// atomic requires 64-bit alignment for struct field access
at int64
@@ -183,10 +188,23 @@ func (f *File) Readdirnames(n int) (names []string, err error) {
return names, err
}
// Implements fs.ReadDirFile
func (f *File) ReadDir(n int) ([]fs.DirEntry, error) {
fi, err := f.Readdir(n)
if err != nil {
return nil, err
}
di := make([]fs.DirEntry, len(fi))
for i, f := range fi {
di[i] = common.FileInfoDirEntry{FileInfo: f}
}
return di, nil
}
func (f *File) Read(b []byte) (n int, err error) {
f.fileData.Lock()
defer f.fileData.Unlock()
if f.closed == true {
if f.closed {
return 0, ErrFileClosed
}
if len(b) > 0 && int(f.at) == len(f.fileData.data) {
@@ -214,7 +232,7 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
}
func (f *File) Truncate(size int64) error {
if f.closed == true {
if f.closed {
return ErrFileClosed
}
if f.readOnly {
@@ -236,7 +254,7 @@ func (f *File) Truncate(size int64) error {
}
func (f *File) Seek(offset int64, whence int) (int64, error) {
if f.closed == true {
if f.closed {
return 0, ErrFileClosed
}
switch whence {
@@ -251,7 +269,7 @@ func (f *File) Seek(offset int64, whence int) (int64, error) {
}
func (f *File) Write(b []byte) (n int, err error) {
if f.closed == true {
if f.closed {
return 0, ErrFileClosed
}
if f.readOnly {
@@ -330,8 +348,8 @@ func (s *FileInfo) Size() int64 {
var (
ErrFileClosed = errors.New("File is closed")
ErrOutOfRange = errors.New("Out of range")
ErrTooLarge = errors.New("Too large")
ErrOutOfRange = errors.New("out of range")
ErrTooLarge = errors.New("too large")
ErrFileNotFound = os.ErrNotExist
ErrFileExists = os.ErrExist
ErrDestinationExists = os.ErrExist

View File

@@ -65,7 +65,7 @@ func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
if f.Layer != nil {
n, err := f.Layer.ReadAt(s, o)
if (err == nil || err == io.EOF) && f.Base != nil {
_, err = f.Base.Seek(o+int64(n), os.SEEK_SET)
_, err = f.Base.Seek(o+int64(n), io.SeekStart)
}
return n, err
}

View File

@@ -25,6 +25,7 @@ import (
"strings"
"unicode"
"golang.org/x/text/runes"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)
@@ -158,16 +159,12 @@ func UnicodeSanitize(s string) string {
// Transform characters with accents into plain forms.
func NeuterAccents(s string) string {
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
result, _, _ := transform.String(t, string(s))
return result
}
func isMn(r rune) bool {
return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
}
func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
return FileContainsBytes(a.Fs, filename, subslice)
}
@@ -299,6 +296,9 @@ func IsEmpty(fs Fs, path string) (bool, error) {
}
defer f.Close()
list, err := f.Readdir(-1)
if err != nil {
return false, err
}
return len(list) == 0, nil
}
return fi.Size() == 0, nil

327
vendor/github.com/spf13/cast/caste.go generated vendored
View File

@@ -34,6 +34,12 @@ func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim time.
return v, nil
case string:
return StringToDateInDefaultLocation(v, location)
case json.Number:
s, err1 := ToInt64E(v)
if err1 != nil {
return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
}
return time.Unix(s, 0), nil
case int:
return time.Unix(int64(v), 0), nil
case int64:
@@ -71,6 +77,11 @@ func ToDurationE(i interface{}) (d time.Duration, err error) {
d, err = time.ParseDuration(s + "ns")
}
return
case json.Number:
var v float64
v, err = s.Float64()
d = time.Duration(v)
return
default:
err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i)
return
@@ -93,6 +104,12 @@ func ToBoolE(i interface{}) (bool, error) {
return false, nil
case string:
return strconv.ParseBool(i.(string))
case json.Number:
v, err := ToInt64E(b)
if err == nil {
return v != 0, nil
}
return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
default:
return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
}
@@ -102,13 +119,16 @@ func ToBoolE(i interface{}) (bool, error) {
func ToFloat64E(i interface{}) (float64, error) {
i = indirect(i)
intv, ok := toInt(i)
if ok {
return float64(intv), nil
}
switch s := i.(type) {
case float64:
return s, nil
case float32:
return float64(s), nil
case int:
return float64(s), nil
case int64:
return float64(s), nil
case int32:
@@ -133,11 +153,19 @@ func ToFloat64E(i interface{}) (float64, error) {
return v, nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
case json.Number:
v, err := s.Float64()
if err == nil {
return v, nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
case bool:
if s {
return 1, nil
}
return 0, nil
case nil:
return 0, nil
default:
return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
}
@@ -147,13 +175,16 @@ func ToFloat64E(i interface{}) (float64, error) {
func ToFloat32E(i interface{}) (float32, error) {
i = indirect(i)
intv, ok := toInt(i)
if ok {
return float32(intv), nil
}
switch s := i.(type) {
case float64:
return float32(s), nil
case float32:
return s, nil
case int:
return float32(s), nil
case int64:
return float32(s), nil
case int32:
@@ -178,11 +209,19 @@ func ToFloat32E(i interface{}) (float32, error) {
return float32(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
case json.Number:
v, err := s.Float64()
if err == nil {
return float32(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
case bool:
if s {
return 1, nil
}
return 0, nil
case nil:
return 0, nil
default:
return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
}
@@ -192,9 +231,12 @@ func ToFloat32E(i interface{}) (float32, error) {
func ToInt64E(i interface{}) (int64, error) {
i = indirect(i)
intv, ok := toInt(i)
if ok {
return int64(intv), nil
}
switch s := i.(type) {
case int:
return int64(s), nil
case int64:
return s, nil
case int32:
@@ -218,11 +260,13 @@ func ToInt64E(i interface{}) (int64, error) {
case float32:
return int64(s), nil
case string:
v, err := strconv.ParseInt(s, 0, 0)
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
return v, nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
case json.Number:
return ToInt64E(string(s))
case bool:
if s {
return 1, nil
@@ -239,9 +283,12 @@ func ToInt64E(i interface{}) (int64, error) {
func ToInt32E(i interface{}) (int32, error) {
i = indirect(i)
intv, ok := toInt(i)
if ok {
return int32(intv), nil
}
switch s := i.(type) {
case int:
return int32(s), nil
case int64:
return int32(s), nil
case int32:
@@ -265,11 +312,13 @@ func ToInt32E(i interface{}) (int32, error) {
case float32:
return int32(s), nil
case string:
v, err := strconv.ParseInt(s, 0, 0)
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
return int32(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i)
case json.Number:
return ToInt32E(string(s))
case bool:
if s {
return 1, nil
@@ -286,9 +335,12 @@ func ToInt32E(i interface{}) (int32, error) {
func ToInt16E(i interface{}) (int16, error) {
i = indirect(i)
intv, ok := toInt(i)
if ok {
return int16(intv), nil
}
switch s := i.(type) {
case int:
return int16(s), nil
case int64:
return int16(s), nil
case int32:
@@ -312,11 +364,13 @@ func ToInt16E(i interface{}) (int16, error) {
case float32:
return int16(s), nil
case string:
v, err := strconv.ParseInt(s, 0, 0)
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
return int16(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i)
case json.Number:
return ToInt16E(string(s))
case bool:
if s {
return 1, nil
@@ -333,9 +387,12 @@ func ToInt16E(i interface{}) (int16, error) {
func ToInt8E(i interface{}) (int8, error) {
i = indirect(i)
intv, ok := toInt(i)
if ok {
return int8(intv), nil
}
switch s := i.(type) {
case int:
return int8(s), nil
case int64:
return int8(s), nil
case int32:
@@ -359,11 +416,13 @@ func ToInt8E(i interface{}) (int8, error) {
case float32:
return int8(s), nil
case string:
v, err := strconv.ParseInt(s, 0, 0)
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
return int8(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i)
case json.Number:
return ToInt8E(string(s))
case bool:
if s {
return 1, nil
@@ -380,9 +439,12 @@ func ToInt8E(i interface{}) (int8, error) {
func ToIntE(i interface{}) (int, error) {
i = indirect(i)
intv, ok := toInt(i)
if ok {
return intv, nil
}
switch s := i.(type) {
case int:
return s, nil
case int64:
return int(s), nil
case int32:
@@ -406,11 +468,13 @@ func ToIntE(i interface{}) (int, error) {
case float32:
return int(s), nil
case string:
v, err := strconv.ParseInt(s, 0, 0)
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
return int(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i)
return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
case json.Number:
return ToIntE(string(s))
case bool:
if s {
return 1, nil
@@ -427,18 +491,26 @@ func ToIntE(i interface{}) (int, error) {
func ToUintE(i interface{}) (uint, error) {
i = indirect(i)
switch s := i.(type) {
case string:
v, err := strconv.ParseUint(s, 0, 0)
if err == nil {
return uint(v), nil
}
return 0, fmt.Errorf("unable to cast %#v to uint: %s", i, err)
case int:
if s < 0 {
intv, ok := toInt(i)
if ok {
if intv < 0 {
return 0, errNegativeNotAllowed
}
return uint(s), nil
return uint(intv), nil
}
switch s := i.(type) {
case string:
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
if v < 0 {
return 0, errNegativeNotAllowed
}
return uint(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i)
case json.Number:
return ToUintE(string(s))
case int64:
if s < 0 {
return 0, errNegativeNotAllowed
@@ -495,18 +567,26 @@ func ToUintE(i interface{}) (uint, error) {
func ToUint64E(i interface{}) (uint64, error) {
i = indirect(i)
switch s := i.(type) {
case string:
v, err := strconv.ParseUint(s, 0, 64)
if err == nil {
return v, nil
}
return 0, fmt.Errorf("unable to cast %#v to uint64: %s", i, err)
case int:
if s < 0 {
intv, ok := toInt(i)
if ok {
if intv < 0 {
return 0, errNegativeNotAllowed
}
return uint64(s), nil
return uint64(intv), nil
}
switch s := i.(type) {
case string:
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
if v < 0 {
return 0, errNegativeNotAllowed
}
return uint64(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i)
case json.Number:
return ToUint64E(string(s))
case int64:
if s < 0 {
return 0, errNegativeNotAllowed
@@ -563,18 +643,26 @@ func ToUint64E(i interface{}) (uint64, error) {
func ToUint32E(i interface{}) (uint32, error) {
i = indirect(i)
switch s := i.(type) {
case string:
v, err := strconv.ParseUint(s, 0, 32)
if err == nil {
return uint32(v), nil
}
return 0, fmt.Errorf("unable to cast %#v to uint32: %s", i, err)
case int:
if s < 0 {
intv, ok := toInt(i)
if ok {
if intv < 0 {
return 0, errNegativeNotAllowed
}
return uint32(s), nil
return uint32(intv), nil
}
switch s := i.(type) {
case string:
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
if v < 0 {
return 0, errNegativeNotAllowed
}
return uint32(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i)
case json.Number:
return ToUint32E(string(s))
case int64:
if s < 0 {
return 0, errNegativeNotAllowed
@@ -631,18 +719,26 @@ func ToUint32E(i interface{}) (uint32, error) {
func ToUint16E(i interface{}) (uint16, error) {
i = indirect(i)
switch s := i.(type) {
case string:
v, err := strconv.ParseUint(s, 0, 16)
if err == nil {
return uint16(v), nil
}
return 0, fmt.Errorf("unable to cast %#v to uint16: %s", i, err)
case int:
if s < 0 {
intv, ok := toInt(i)
if ok {
if intv < 0 {
return 0, errNegativeNotAllowed
}
return uint16(s), nil
return uint16(intv), nil
}
switch s := i.(type) {
case string:
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
if v < 0 {
return 0, errNegativeNotAllowed
}
return uint16(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i)
case json.Number:
return ToUint16E(string(s))
case int64:
if s < 0 {
return 0, errNegativeNotAllowed
@@ -699,18 +795,26 @@ func ToUint16E(i interface{}) (uint16, error) {
func ToUint8E(i interface{}) (uint8, error) {
i = indirect(i)
switch s := i.(type) {
case string:
v, err := strconv.ParseUint(s, 0, 8)
if err == nil {
return uint8(v), nil
}
return 0, fmt.Errorf("unable to cast %#v to uint8: %s", i, err)
case int:
if s < 0 {
intv, ok := toInt(i)
if ok {
if intv < 0 {
return 0, errNegativeNotAllowed
}
return uint8(s), nil
return uint8(intv), nil
}
switch s := i.(type) {
case string:
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
if v < 0 {
return 0, errNegativeNotAllowed
}
return uint8(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i)
case json.Number:
return ToUint8E(string(s))
case int64:
if s < 0 {
return 0, errNegativeNotAllowed
@@ -835,6 +939,8 @@ func ToStringE(i interface{}) (string, error) {
return strconv.FormatUint(uint64(s), 10), nil
case uint8:
return strconv.FormatUint(uint64(s), 10), nil
case json.Number:
return s.String(), nil
case []byte:
return string(s), nil
case template.HTML:
@@ -1279,30 +1385,30 @@ func (f timeFormat) hasTimezone() bool {
var (
timeFormats = []timeFormat{
timeFormat{time.RFC3339, timeFormatNumericTimezone},
timeFormat{"2006-01-02T15:04:05", timeFormatNoTimezone}, // iso8601 without timezone
timeFormat{time.RFC1123Z, timeFormatNumericTimezone},
timeFormat{time.RFC1123, timeFormatNamedTimezone},
timeFormat{time.RFC822Z, timeFormatNumericTimezone},
timeFormat{time.RFC822, timeFormatNamedTimezone},
timeFormat{time.RFC850, timeFormatNamedTimezone},
timeFormat{"2006-01-02 15:04:05.999999999 -0700 MST", timeFormatNumericAndNamedTimezone}, // Time.String()
timeFormat{"2006-01-02T15:04:05-0700", timeFormatNumericTimezone}, // RFC3339 without timezone hh:mm colon
timeFormat{"2006-01-02 15:04:05Z0700", timeFormatNumericTimezone}, // RFC3339 without T or timezone hh:mm colon
timeFormat{"2006-01-02 15:04:05", timeFormatNoTimezone},
timeFormat{time.ANSIC, timeFormatNoTimezone},
timeFormat{time.UnixDate, timeFormatNamedTimezone},
timeFormat{time.RubyDate, timeFormatNumericTimezone},
timeFormat{"2006-01-02 15:04:05Z07:00", timeFormatNumericTimezone},
timeFormat{"2006-01-02", timeFormatNoTimezone},
timeFormat{"02 Jan 2006", timeFormatNoTimezone},
timeFormat{"2006-01-02 15:04:05 -07:00", timeFormatNumericTimezone},
timeFormat{"2006-01-02 15:04:05 -0700", timeFormatNumericTimezone},
timeFormat{time.Kitchen, timeFormatTimeOnly},
timeFormat{time.Stamp, timeFormatTimeOnly},
timeFormat{time.StampMilli, timeFormatTimeOnly},
timeFormat{time.StampMicro, timeFormatTimeOnly},
timeFormat{time.StampNano, timeFormatTimeOnly},
{time.RFC3339, timeFormatNumericTimezone},
{"2006-01-02T15:04:05", timeFormatNoTimezone}, // iso8601 without timezone
{time.RFC1123Z, timeFormatNumericTimezone},
{time.RFC1123, timeFormatNamedTimezone},
{time.RFC822Z, timeFormatNumericTimezone},
{time.RFC822, timeFormatNamedTimezone},
{time.RFC850, timeFormatNamedTimezone},
{"2006-01-02 15:04:05.999999999 -0700 MST", timeFormatNumericAndNamedTimezone}, // Time.String()
{"2006-01-02T15:04:05-0700", timeFormatNumericTimezone}, // RFC3339 without timezone hh:mm colon
{"2006-01-02 15:04:05Z0700", timeFormatNumericTimezone}, // RFC3339 without T or timezone hh:mm colon
{"2006-01-02 15:04:05", timeFormatNoTimezone},
{time.ANSIC, timeFormatNoTimezone},
{time.UnixDate, timeFormatNamedTimezone},
{time.RubyDate, timeFormatNumericTimezone},
{"2006-01-02 15:04:05Z07:00", timeFormatNumericTimezone},
{"2006-01-02", timeFormatNoTimezone},
{"02 Jan 2006", timeFormatNoTimezone},
{"2006-01-02 15:04:05 -07:00", timeFormatNumericTimezone},
{"2006-01-02 15:04:05 -0700", timeFormatNumericTimezone},
{time.Kitchen, timeFormatTimeOnly},
{time.Stamp, timeFormatTimeOnly},
{time.StampMilli, timeFormatTimeOnly},
{time.StampMicro, timeFormatTimeOnly},
{time.StampNano, timeFormatTimeOnly},
}
)
@@ -1335,3 +1441,36 @@ func jsonStringToObject(s string, v interface{}) error {
data := []byte(s)
return json.Unmarshal(data, v)
}
// toInt returns the int value of v if v or v's underlying type
// is an int.
// Note that this will return false for int64 etc. types.
func toInt(v interface{}) (int, bool) {
switch v := v.(type) {
case int:
return v, true
case time.Weekday:
return int(v), true
case time.Month:
return int(v), true
default:
return 0, false
}
}
func trimZeroDecimal(s string) string {
var foundZero bool
for i := len(s); i > 0; i-- {
switch s[i-1] {
case '.':
if foundZero {
return s[:i-1]
}
case '0':
foundZero = true
default:
return s
}
}
return s
}

View File

@@ -11,5 +11,5 @@ trim_trailing_whitespace = true
[*.go]
indent_style = tab
[{Makefile, *.mk}]
[{Makefile,*.mk}]
indent_style = tab

View File

@@ -3,7 +3,10 @@ run:
linters-settings:
gci:
local-prefixes: github.com/spf13/viper
sections:
- standard
- default
- prefix(github.com/spf13/viper)
golint:
min-confidence: 0
goimports:

View File

@@ -15,8 +15,8 @@ TEST_FORMAT = short-verbose
endif
# Dependency versions
GOTESTSUM_VERSION = 1.7.0
GOLANGCI_VERSION = 1.43.0
GOTESTSUM_VERSION = 1.8.0
GOLANGCI_VERSION = 1.49.0
# Add the ability to override some variables
# Use with care
@@ -48,7 +48,7 @@ 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://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION}
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

View File

@@ -11,7 +11,7 @@
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/spf13/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/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.14-61CFDD.svg?style=flat-square)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.15-61CFDD.svg?style=flat-square)
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)
**Go configuration with fangs!**
@@ -119,7 +119,7 @@ viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search
viper.AddConfigPath(".") // optionally look for config in the working directory
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
panic(fmt.Errorf("Fatal error config file: %w \n", err))
panic(fmt.Errorf("fatal error config file: %w", err))
}
```
@@ -447,6 +447,13 @@ viper.SetConfigType("json") // because there is no file extension in a stream of
err := viper.ReadRemoteConfig()
```
#### etcd3
```go
viper.AddRemoteProvider("etcd3", "http://127.0.0.1:4001","/config/hugo.json")
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
err := viper.ReadRemoteConfig()
```
#### Consul
You need to set a key to Consul key/value storage with JSON value containing your desired config.
For example, create a Consul key/value store key `MY_CONSUL_KEY` with value:
@@ -594,7 +601,7 @@ configuration level.
Viper can access array indices by using numbers in the path. For example:
```json
```jsonc
{
"host": {
"address": "localhost",
@@ -622,7 +629,7 @@ GetInt("host.ports.1") // returns 6029
Lastly, if there exists a key that matches the delimited key path, its value
will be returned instead. E.g.
```json
```jsonc
{
"datastore.metric.host": "0.0.0.0",
"host": {

View File

@@ -21,3 +21,12 @@ The solution is easy: switch to using Go Modules.
Please refer to the [wiki](https://github.com/golang/go/wiki/Modules) on how to do that.
**tl;dr* `export GO111MODULE=on`
## Unquoted 'y' and 'n' characters get replaced with _true_ and _false_ when reading a YAML file
This is a YAML 1.1 feature according to [go-yaml/yaml#740](https://github.com/go-yaml/yaml/issues/740).
Potential solutions are:
1. Quoting values resolved as boolean
1. Upgrading to YAML v3 (for the time being this is possible by passing the `viper_yaml3` tag to your build)

11
vendor/github.com/spf13/viper/experimental_logger.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
//go:build viper_logger
// +build viper_logger
package viper
// WithLogger sets a custom logger.
func WithLogger(l Logger) Option {
return optionFunc(func(v *Viper) {
v.logger = l
})
}

View File

@@ -4,10 +4,10 @@ import (
"sync"
)
// Decoder decodes the contents of b into a v representation.
// 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 interface{}) error
Decode(b []byte, v map[string]interface{}) error
}
const (
@@ -48,7 +48,7 @@ func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
}
// Decode calls the underlying Decoder based on the format.
func (e *DecoderRegistry) Decode(format string, b []byte, v interface{}) error {
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
e.mu.RLock()
decoder, ok := e.decoders[format]
e.mu.RUnlock()

View File

@@ -0,0 +1,61 @@
package dotenv
import (
"bytes"
"fmt"
"sort"
"strings"
"github.com/subosito/gotenv"
)
const keyDelimiter = "_"
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for encoding data containing environment variables
// (commonly called as dotenv format).
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
flattened := map[string]interface{}{}
flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter)
keys := make([]string, 0, len(flattened))
for key := range flattened {
keys = append(keys, key)
}
sort.Strings(keys)
var buf bytes.Buffer
for _, key := range keys {
_, err := buf.WriteString(fmt.Sprintf("%v=%v\n", strings.ToUpper(key), flattened[key]))
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
var buf bytes.Buffer
_, err := buf.Write(b)
if err != nil {
return err
}
env, err := gotenv.StrictParse(&buf)
if err != nil {
return err
}
for key, value := range env {
v[key] = value
}
return nil
}

View File

@@ -0,0 +1,41 @@
package dotenv
import (
"strings"
"github.com/spf13/cast"
)
// flattenAndMergeMap recursively flattens the given map into a new map
// Code is based on the function with the same name in tha main package.
// TODO: move it to a common place
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten
return shadow
}
if shadow == nil {
shadow = make(map[string]interface{})
}
var m2 map[string]interface{}
if prefix != "" {
prefix += delimiter
}
for k, val := range m {
fullKey := prefix + k
switch val.(type) {
case map[string]interface{}:
m2 = val.(map[string]interface{})
case map[interface{}]interface{}:
m2 = cast.ToStringMap(val)
default:
// immediate value
shadow[strings.ToLower(fullKey)] = val
continue
}
// recursively merge to shadow map
shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
}
return shadow
}

View File

@@ -7,7 +7,7 @@ import (
// 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 interface{}) ([]byte, error)
Encode(v map[string]interface{}) ([]byte, error)
}
const (
@@ -47,7 +47,7 @@ func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error {
return nil
}
func (e *EncoderRegistry) Encode(format string, v interface{}) ([]byte, error) {
func (e *EncoderRegistry) Encode(format string, v map[string]interface{}) ([]byte, error) {
e.mu.RLock()
encoder, ok := e.encoders[format]
e.mu.RUnlock()

View File

@@ -12,7 +12,7 @@ import (
// TODO: add printer config to the codec?
type Codec struct{}
func (Codec) Encode(v interface{}) ([]byte, error) {
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, err
@@ -35,6 +35,6 @@ func (Codec) Encode(v interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (Codec) Decode(b []byte, v interface{}) error {
return hcl.Unmarshal(b, v)
func (Codec) Decode(b []byte, v map[string]interface{}) error {
return hcl.Unmarshal(b, &v)
}

View File

@@ -0,0 +1,99 @@
package ini
import (
"bytes"
"sort"
"strings"
"github.com/spf13/cast"
"gopkg.in/ini.v1"
)
// LoadOptions contains all customized options used for load data source(s).
// This type is added here for convenience: this way consumers can import a single package called "ini".
type LoadOptions = ini.LoadOptions
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for INI encoding.
type Codec struct {
KeyDelimiter string
LoadOptions LoadOptions
}
func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
cfg := ini.Empty()
ini.PrettyFormat = false
flattened := map[string]interface{}{}
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
keys := make([]string, 0, len(flattened))
for key := range flattened {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
sectionName, keyName := "", key
lastSep := strings.LastIndex(key, ".")
if lastSep != -1 {
sectionName = key[:(lastSep)]
keyName = key[(lastSep + 1):]
}
// TODO: is this a good idea?
if sectionName == "default" {
sectionName = ""
}
cfg.Section(sectionName).Key(keyName).SetValue(cast.ToString(flattened[key]))
}
var buf bytes.Buffer
_, err := cfg.WriteTo(&buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (c Codec) Decode(b []byte, v map[string]interface{}) error {
cfg := ini.Empty(c.LoadOptions)
err := cfg.Append(b)
if err != nil {
return err
}
sections := cfg.Sections()
for i := 0; i < len(sections); i++ {
section := sections[i]
keys := section.Keys()
for j := 0; j < len(keys); j++ {
key := keys[j]
value := cfg.Section(section.Name()).Key(key.Name()).String()
deepestMap := deepSearch(v, strings.Split(section.Name(), c.keyDelimiter()))
// set innermost value
deepestMap[key.Name()] = value
}
}
return nil
}
func (c Codec) keyDelimiter() string {
if c.KeyDelimiter == "" {
return "."
}
return c.KeyDelimiter
}

View File

@@ -0,0 +1,74 @@
package ini
import (
"strings"
"github.com/spf13/cast"
)
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE
// deepSearch scans deep maps, following the key indexes listed in the
// sequence "path".
// The last value is expected to be another map, and is returned.
//
// In case intermediate keys do not exist, or map to a non-map value,
// a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified!
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
for _, k := range path {
m2, ok := m[k]
if !ok {
// intermediate key does not exist
// => create it and continue from there
m3 := make(map[string]interface{})
m[k] = m3
m = m3
continue
}
m3, ok := m2.(map[string]interface{})
if !ok {
// intermediate key is a value
// => replace with a new map
m3 = make(map[string]interface{})
m[k] = m3
}
// continue search from here
m = m3
}
return m
}
// flattenAndMergeMap recursively flattens the given map into a new map
// Code is based on the function with the same name in tha main package.
// TODO: move it to a common place
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten
return shadow
}
if shadow == nil {
shadow = make(map[string]interface{})
}
var m2 map[string]interface{}
if prefix != "" {
prefix += delimiter
}
for k, val := range m {
fullKey := prefix + k
switch val.(type) {
case map[string]interface{}:
m2 = val.(map[string]interface{})
case map[interface{}]interface{}:
m2 = cast.ToStringMap(val)
default:
// immediate value
shadow[strings.ToLower(fullKey)] = val
continue
}
// recursively merge to shadow map
shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
}
return shadow
}

View File

@@ -0,0 +1,86 @@
package javaproperties
import (
"bytes"
"sort"
"strings"
"github.com/magiconair/properties"
"github.com/spf13/cast"
)
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding.
type Codec struct {
KeyDelimiter string
// Store read properties on the object so that we can write back in order with comments.
// This will only be used if the configuration read is a properties file.
// TODO: drop this feature in v2
// TODO: make use of the global properties object optional
Properties *properties.Properties
}
func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
if c.Properties == nil {
c.Properties = properties.NewProperties()
}
flattened := map[string]interface{}{}
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
keys := make([]string, 0, len(flattened))
for key := range flattened {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
_, _, err := c.Properties.Set(key, cast.ToString(flattened[key]))
if err != nil {
return nil, err
}
}
var buf bytes.Buffer
_, err := c.Properties.WriteComment(&buf, "#", properties.UTF8)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
var err error
c.Properties, err = properties.Load(b, properties.UTF8)
if err != nil {
return err
}
for _, key := range c.Properties.Keys() {
// ignore existence check: we know it's there
value, _ := c.Properties.Get(key)
// recursively build nested maps
path := strings.Split(key, c.keyDelimiter())
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(v, path[0:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
}
return nil
}
func (c Codec) keyDelimiter() string {
if c.KeyDelimiter == "" {
return "."
}
return c.KeyDelimiter
}

View File

@@ -0,0 +1,74 @@
package javaproperties
import (
"strings"
"github.com/spf13/cast"
)
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE
// deepSearch scans deep maps, following the key indexes listed in the
// sequence "path".
// The last value is expected to be another map, and is returned.
//
// In case intermediate keys do not exist, or map to a non-map value,
// a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified!
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
for _, k := range path {
m2, ok := m[k]
if !ok {
// intermediate key does not exist
// => create it and continue from there
m3 := make(map[string]interface{})
m[k] = m3
m = m3
continue
}
m3, ok := m2.(map[string]interface{})
if !ok {
// intermediate key is a value
// => replace with a new map
m3 = make(map[string]interface{})
m[k] = m3
}
// continue search from here
m = m3
}
return m
}
// flattenAndMergeMap recursively flattens the given map into a new map
// Code is based on the function with the same name in tha main package.
// TODO: move it to a common place
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
if shadow != nil && prefix != "" && shadow[prefix] != nil {
// prefix is shadowed => nothing more to flatten
return shadow
}
if shadow == nil {
shadow = make(map[string]interface{})
}
var m2 map[string]interface{}
if prefix != "" {
prefix += delimiter
}
for k, val := range m {
fullKey := prefix + k
switch val.(type) {
case map[string]interface{}:
m2 = val.(map[string]interface{})
case map[interface{}]interface{}:
m2 = cast.ToStringMap(val)
default:
// immediate value
shadow[strings.ToLower(fullKey)] = val
continue
}
// recursively merge to shadow map
shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
}
return shadow
}

View File

@@ -7,11 +7,11 @@ import (
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
type Codec struct{}
func (Codec) Encode(v interface{}) ([]byte, error) {
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 interface{}) error {
return json.Unmarshal(b, v)
func (Codec) Decode(b []byte, v map[string]interface{}) error {
return json.Unmarshal(b, &v)
}

View File

@@ -1,3 +1,6 @@
//go:build viper_toml1
// +build viper_toml1
package toml
import (
@@ -7,39 +10,30 @@ import (
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
type Codec struct{}
func (Codec) Encode(v interface{}) ([]byte, error) {
if m, ok := v.(map[string]interface{}); ok {
t, err := toml.TreeFromMap(m)
if err != nil {
return nil, err
}
s, err := t.ToTomlString()
if err != nil {
return nil, err
}
return []byte(s), nil
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
t, err := toml.TreeFromMap(v)
if err != nil {
return nil, err
}
return toml.Marshal(v)
s, err := t.ToTomlString()
if err != nil {
return nil, err
}
return []byte(s), nil
}
func (Codec) Decode(b []byte, v interface{}) error {
func (Codec) Decode(b []byte, v map[string]interface{}) error {
tree, err := toml.LoadBytes(b)
if err != nil {
return err
}
if m, ok := v.(*map[string]interface{}); ok {
vmap := *m
tmap := tree.ToMap()
for k, v := range tmap {
vmap[k] = v
}
return nil
tmap := tree.ToMap()
for key, value := range tmap {
v[key] = value
}
return tree.Unmarshal(v)
return nil
}

View File

@@ -0,0 +1,19 @@
//go:build !viper_toml1
// +build !viper_toml1
package toml
import (
"github.com/pelletier/go-toml/v2"
)
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return toml.Marshal(v)
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
return toml.Unmarshal(b, &v)
}

View File

@@ -1,14 +1,14 @@
package yaml
import "gopkg.in/yaml.v2"
// import "gopkg.in/yaml.v2"
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
type Codec struct{}
func (Codec) Encode(v interface{}) ([]byte, error) {
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return yaml.Marshal(v)
}
func (Codec) Decode(b []byte, v interface{}) error {
return yaml.Unmarshal(b, v)
func (Codec) Decode(b []byte, v map[string]interface{}) error {
return yaml.Unmarshal(b, &v)
}

View File

@@ -0,0 +1,14 @@
//go:build viper_yaml2
// +build viper_yaml2
package yaml
import yamlv2 "gopkg.in/yaml.v2"
var yaml = struct {
Marshal func(in interface{}) (out []byte, err error)
Unmarshal func(in []byte, out interface{}) (err error)
}{
Marshal: yamlv2.Marshal,
Unmarshal: yamlv2.Unmarshal,
}

View File

@@ -0,0 +1,14 @@
//go:build !viper_yaml2
// +build !viper_yaml2
package yaml
import yamlv3 "gopkg.in/yaml.v3"
var yaml = struct {
Marshal func(in interface{}) (out []byte, err error)
Unmarshal func(in []byte, out interface{}) (err error)
}{
Marshal: yamlv3.Marshal,
Unmarshal: yamlv3.Unmarshal,
}

View File

@@ -7,8 +7,8 @@ import (
)
// Logger is a unified interface for various logging use cases and practices, including:
// - leveled logging
// - structured logging
// - leveled logging
// - structured logging
type Logger interface {
// Trace logs a Trace event.
//

View File

@@ -64,18 +64,25 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
return nm
}
func insensitiviseVal(val interface{}) interface{} {
switch val.(type) {
case map[interface{}]interface{}:
// nested map: cast and recursively insensitivise
val = cast.ToStringMap(val)
insensitiviseMap(val.(map[string]interface{}))
case map[string]interface{}:
// nested map: recursively insensitivise
insensitiviseMap(val.(map[string]interface{}))
case []interface{}:
// nested array: recursively insensitivise
insensitiveArray(val.([]interface{}))
}
return val
}
func insensitiviseMap(m map[string]interface{}) {
for key, val := range m {
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{}))
}
val = insensitiviseVal(val)
lower := strings.ToLower(key)
if key != lower {
// remove old key (not lower-cased)
@@ -86,6 +93,12 @@ func insensitiviseMap(m map[string]interface{}) {
}
}
func insensitiveArray(a []interface{}) {
for i, val := range a {
a[i] = insensitiviseVal(val)
}
}
func absPathify(logger Logger, inPath string) string {
logger.Info("trying to resolve absolute path", "path", inPath)

View File

@@ -35,16 +35,16 @@ import (
"time"
"github.com/fsnotify/fsnotify"
"github.com/magiconair/properties"
"github.com/mitchellh/mapstructure"
"github.com/spf13/afero"
"github.com/spf13/cast"
"github.com/spf13/pflag"
"github.com/subosito/gotenv"
"gopkg.in/ini.v1"
"github.com/spf13/viper/internal/encoding"
"github.com/spf13/viper/internal/encoding/dotenv"
"github.com/spf13/viper/internal/encoding/hcl"
"github.com/spf13/viper/internal/encoding/ini"
"github.com/spf13/viper/internal/encoding/javaproperties"
"github.com/spf13/viper/internal/encoding/json"
"github.com/spf13/viper/internal/encoding/toml"
"github.com/spf13/viper/internal/encoding/yaml"
@@ -67,47 +67,8 @@ type RemoteResponse struct {
Error error
}
var (
encoderRegistry = encoding.NewEncoderRegistry()
decoderRegistry = encoding.NewDecoderRegistry()
)
func init() {
v = New()
{
codec := yaml.Codec{}
encoderRegistry.RegisterEncoder("yaml", codec)
decoderRegistry.RegisterDecoder("yaml", codec)
encoderRegistry.RegisterEncoder("yml", codec)
decoderRegistry.RegisterDecoder("yml", codec)
}
{
codec := json.Codec{}
encoderRegistry.RegisterEncoder("json", codec)
decoderRegistry.RegisterDecoder("json", codec)
}
{
codec := toml.Codec{}
encoderRegistry.RegisterEncoder("toml", codec)
decoderRegistry.RegisterDecoder("toml", codec)
}
{
codec := hcl.Codec{}
encoderRegistry.RegisterEncoder("hcl", codec)
decoderRegistry.RegisterDecoder("hcl", codec)
encoderRegistry.RegisterEncoder("tfvars", codec)
decoderRegistry.RegisterDecoder("tfvars", codec)
}
}
type remoteConfigFactory interface {
@@ -171,10 +132,10 @@ type DecoderConfigOption func(*mapstructure.DecoderConfig)
// DecodeHook returns a DecoderConfigOption which overrides the default
// DecoderConfig.DecodeHook value, the default is:
//
// mapstructure.ComposeDecodeHookFunc(
// mapstructure.StringToTimeDurationHookFunc(),
// mapstructure.StringToSliceHookFunc(","),
// )
// mapstructure.ComposeDecodeHookFunc(
// mapstructure.StringToTimeDurationHookFunc(),
// mapstructure.StringToSliceHookFunc(","),
// )
func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
return func(c *mapstructure.DecoderConfig) {
c.DecodeHook = hook
@@ -195,18 +156,18 @@ func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
//
// For example, if values from the following sources were loaded:
//
// Defaults : {
// "secret": "",
// "user": "default",
// "endpoint": "https://localhost"
// }
// Config : {
// "user": "root"
// "secret": "defaultsecret"
// }
// Env : {
// "secret": "somesecretkey"
// }
// Defaults : {
// "secret": "",
// "user": "default",
// "endpoint": "https://localhost"
// }
// Config : {
// "user": "root"
// "secret": "defaultsecret"
// }
// Env : {
// "secret": "somesecretkey"
// }
//
// The resulting config will have the following values:
//
@@ -254,13 +215,13 @@ type Viper struct {
aliases map[string]string
typeByDefValue bool
// 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.
properties *properties.Properties
onConfigChange func(fsnotify.Event)
logger Logger
// TODO: should probably be protected with a mutex
encoderRegistry *encoding.EncoderRegistry
decoderRegistry *encoding.DecoderRegistry
}
// New returns an initialized Viper instance.
@@ -280,6 +241,8 @@ func New() *Viper {
v.typeByDefValue = false
v.logger = jwwLogger{}
v.resetEncoding()
return v
}
@@ -326,6 +289,8 @@ func NewWithOptions(opts ...Option) *Viper {
opt.apply(v)
}
v.resetEncoding()
return v
}
@@ -335,7 +300,85 @@ func NewWithOptions(opts ...Option) *Viper {
func Reset() {
v = New()
SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
}
// TODO: make this lazy initialization instead
func (v *Viper) resetEncoding() {
encoderRegistry := encoding.NewEncoderRegistry()
decoderRegistry := encoding.NewDecoderRegistry()
{
codec := yaml.Codec{}
encoderRegistry.RegisterEncoder("yaml", codec)
decoderRegistry.RegisterDecoder("yaml", codec)
encoderRegistry.RegisterEncoder("yml", codec)
decoderRegistry.RegisterDecoder("yml", codec)
}
{
codec := json.Codec{}
encoderRegistry.RegisterEncoder("json", codec)
decoderRegistry.RegisterDecoder("json", codec)
}
{
codec := toml.Codec{}
encoderRegistry.RegisterEncoder("toml", codec)
decoderRegistry.RegisterDecoder("toml", codec)
}
{
codec := hcl.Codec{}
encoderRegistry.RegisterEncoder("hcl", codec)
decoderRegistry.RegisterDecoder("hcl", codec)
encoderRegistry.RegisterEncoder("tfvars", codec)
decoderRegistry.RegisterDecoder("tfvars", codec)
}
{
codec := ini.Codec{
KeyDelimiter: v.keyDelim,
LoadOptions: v.iniLoadOptions,
}
encoderRegistry.RegisterEncoder("ini", codec)
decoderRegistry.RegisterDecoder("ini", codec)
}
{
codec := &javaproperties.Codec{
KeyDelimiter: v.keyDelim,
}
encoderRegistry.RegisterEncoder("properties", codec)
decoderRegistry.RegisterDecoder("properties", codec)
encoderRegistry.RegisterEncoder("props", codec)
decoderRegistry.RegisterDecoder("props", codec)
encoderRegistry.RegisterEncoder("prop", codec)
decoderRegistry.RegisterDecoder("prop", codec)
}
{
codec := &dotenv.Codec{}
encoderRegistry.RegisterEncoder("dotenv", codec)
decoderRegistry.RegisterDecoder("dotenv", codec)
encoderRegistry.RegisterEncoder("env", codec)
decoderRegistry.RegisterDecoder("env", codec)
}
v.encoderRegistry = encoderRegistry
v.decoderRegistry = decoderRegistry
}
type defaultRemoteProvider struct {
@@ -376,7 +419,7 @@ type RemoteProvider interface {
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
// SupportedRemoteProviders are universally supported remote providers.
var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
@@ -433,7 +476,7 @@ func (v *Viper) WatchConfig() {
v.onConfigChange(event)
}
} else if filepath.Clean(event.Name) == configFile &&
event.Op&fsnotify.Remove&fsnotify.Remove != 0 {
event.Op&fsnotify.Remove != 0 {
eventsWG.Done()
return
}
@@ -530,7 +573,7 @@ func (v *Viper) AddConfigPath(in string) {
// AddRemoteProvider adds a remote configuration source.
// Remote Providers are searched in the order they are added.
// provider is a string value: "etcd", "consul" or "firestore" are currently supported.
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
// path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json
@@ -561,7 +604,7 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
// AddSecureRemoteProvider adds a remote configuration source.
// Secure Remote Providers are searched in the order they are added.
// provider is a string value: "etcd", "consul" or "firestore" are currently supported.
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg
// path is the path in the k/v store to retrieve configuration
@@ -742,7 +785,8 @@ func (v *Viper) searchMapWithPathPrefixes(
// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
// on its path in the map.
// e.g., if "foo.bar" has a value in the given map, it “shadows”
// "foo.bar.baz" in a lower-priority map
//
// "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
var parentVal interface{}
for i := 1; i < len(path); i++ {
@@ -767,7 +811,8 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere
// in a sub-path of the map.
// e.g., if "foo.bar" has a value in the given map, it “shadows”
// "foo.bar.baz" in a lower-priority map
//
// "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
// unify input map
var m map[string]interface{}
@@ -792,7 +837,8 @@ func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere
// in the environment, when automatic env is on.
// e.g., if "foo.bar" has a value in the environment, it “shadows”
// "foo.bar.baz" in a lower-priority map
//
// "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
var parentKey string
for i := 1; i < len(path); i++ {
@@ -813,11 +859,11 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
// would return a string slice for the key if the key's type is inferred by
// the default value and the Get function would return:
//
// []string {"a", "b", "c"}
// []string {"a", "b", "c"}
//
// Otherwise the Get function would return:
//
// "a b c"
// "a b c"
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
func (v *Viper) SetTypeByDefaultValue(enable bool) {
@@ -945,6 +991,13 @@ func (v *Viper) GetUint(key string) uint {
return cast.ToUint(v.Get(key))
}
// GetUint16 returns the value associated with the key as an unsigned integer.
func GetUint16(key string) uint16 { return v.GetUint16(key) }
func (v *Viper) GetUint16(key string) uint16 {
return cast.ToUint16(v.Get(key))
}
// GetUint32 returns the value associated with the key as an unsigned integer.
func GetUint32(key string) uint32 { return v.GetUint32(key) }
@@ -1094,9 +1147,8 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
// BindPFlag binds a specific key to a pflag (as used by cobra).
// Example (where serverCmd is a Cobra instance):
//
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
//
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
@@ -1154,6 +1206,17 @@ func (v *Viper) BindEnv(input ...string) error {
return nil
}
// MustBindEnv wraps BindEnv in a panic.
// If there is an error binding an environment variable, MustBindEnv will
// panic.
func MustBindEnv(input ...string) { v.MustBindEnv(input...) }
func (v *Viper) MustBindEnv(input ...string) {
if err := v.BindEnv(input...); err != nil {
panic(fmt.Sprintf("error while binding environment variable: %v", err))
}
}
// Given a key, find the value.
//
// Viper will check to see if an alias exists first.
@@ -1634,53 +1697,11 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
buf.ReadFrom(in)
switch format := strings.ToLower(v.getConfigType()); format {
case "yaml", "yml", "json", "toml", "hcl", "tfvars":
err := decoderRegistry.Decode(format, buf.Bytes(), &c)
case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env":
err := v.decoderRegistry.Decode(format, buf.Bytes(), c)
if err != nil {
return ConfigParseError{err}
}
case "dotenv", "env":
env, err := gotenv.StrictParse(buf)
if err != nil {
return ConfigParseError{err}
}
for k, v := range env {
c[k] = v
}
case "properties", "props", "prop":
v.properties = properties.NewProperties()
var err error
if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
return ConfigParseError{err}
}
for _, key := range v.properties.Keys() {
value, _ := v.properties.Get(key)
// recursively build nested maps
path := strings.Split(key, ".")
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(c, path[0:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
}
case "ini":
cfg := ini.Empty(v.iniLoadOptions)
err := cfg.Append(buf.Bytes())
if err != nil {
return ConfigParseError{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()
c[section.Name()+"."+key.Name()] = value
}
}
}
insensitiviseMap(c)
@@ -1691,8 +1712,8 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
func (v *Viper) marshalWriter(f afero.File, configType string) error {
c := v.AllSettings()
switch configType {
case "yaml", "yml", "json", "toml", "hcl", "tfvars":
b, err := encoderRegistry.Encode(configType, c)
case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "prop", "props", "properties", "dotenv", "env":
b, err := v.encoderRegistry.Encode(configType, c)
if err != nil {
return ConfigMarshalError{err}
}
@@ -1701,50 +1722,6 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error {
if err != nil {
return ConfigMarshalError{err}
}
case "prop", "props", "properties":
if v.properties == nil {
v.properties = properties.NewProperties()
}
p := v.properties
for _, key := range v.AllKeys() {
_, _, err := p.Set(key, v.GetString(key))
if err != nil {
return ConfigMarshalError{err}
}
}
_, err := p.WriteComment(f, "#", properties.UTF8)
if err != nil {
return ConfigMarshalError{err}
}
case "dotenv", "env":
lines := []string{}
for _, key := range v.AllKeys() {
envName := strings.ToUpper(strings.Replace(key, ".", "_", -1))
val := v.Get(key)
lines = append(lines, fmt.Sprintf("%v=%v", envName, val))
}
s := strings.Join(lines, "\n")
if _, err := f.WriteString(s); err != nil {
return ConfigMarshalError{err}
}
case "ini":
keys := v.AllKeys()
cfg := ini.Empty()
ini.PrettyFormat = false
for i := 0; i < len(keys); i++ {
key := keys[i]
lastSep := strings.LastIndex(key, ".")
sectionName := key[:(lastSep)]
keyName := key[(lastSep + 1):]
if sectionName == "default" {
sectionName = ""
}
cfg.Section(sectionName).Key(keyName).SetValue(v.GetString(key))
}
cfg.WriteTo(f)
}
return nil
}
@@ -1761,7 +1738,8 @@ func keyExists(k string, m map[string]interface{}) string {
}
func castToMapStringInterface(
src map[interface{}]interface{}) map[string]interface{} {
src map[interface{}]interface{},
) map[string]interface{} {
tgt := map[string]interface{}{}
for k, v := range src {
tgt[fmt.Sprintf("%v", k)] = v
@@ -1799,7 +1777,8 @@ func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{}
// deep. Both map types are supported as there is a go-yaml fork that uses
// `map[string]interface{}` instead.
func mergeMaps(
src, tgt map[string]interface{}, itgt map[interface{}]interface{}) {
src, tgt map[string]interface{}, itgt map[interface{}]interface{},
) {
for sk, sv := range src {
tk := keyExists(sk, tgt)
if tk == "" {
@@ -1823,17 +1802,6 @@ func mergeMaps(
svType := reflect.TypeOf(sv)
tvType := reflect.TypeOf(tv)
if tvType != nil && svType != tvType { // Allow for the target to be nil
v.logger.Error(
"svType != tvType",
"key", sk,
"st", svType,
"tt", tvType,
"sv", sv,
"tv", tv,
)
continue
}
v.logger.Trace(
"processing",
@@ -1847,13 +1815,37 @@ func mergeMaps(
switch ttv := tv.(type) {
case map[interface{}]interface{}:
v.logger.Trace("merging maps (must convert)")
tsv := sv.(map[interface{}]interface{})
tsv, ok := sv.(map[interface{}]interface{})
if !ok {
v.logger.Error(
"Could not cast sv to map[interface{}]interface{}",
"key", sk,
"st", svType,
"tt", tvType,
"sv", sv,
"tv", tv,
)
continue
}
ssv := castToMapStringInterface(tsv)
stv := castToMapStringInterface(ttv)
mergeMaps(ssv, stv, ttv)
case map[string]interface{}:
v.logger.Trace("merging maps")
mergeMaps(sv.(map[string]interface{}), ttv, nil)
tsv, ok := sv.(map[string]interface{})
if !ok {
v.logger.Error(
"Could not cast sv to map[string]interface{}",
"key", sk,
"st", svType,
"tt", tvType,
"sv", sv,
"tv", tv,
)
continue
}
mergeMaps(tsv, ttv, nil)
default:
v.logger.Trace("setting value")
tgt[tk] = sv
@@ -1887,6 +1879,10 @@ func (v *Viper) getKeyValueConfig() error {
return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
}
if len(v.remoteProviders) == 0 {
return RemoteConfigError("No Remote Providers")
}
for _, rp := range v.remoteProviders {
val, err := v.getRemoteConfig(rp)
if err != nil {
@@ -1913,6 +1909,10 @@ func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}
// Retrieve the first found remote configuration.
func (v *Viper) watchKeyValueConfigOnChannel() error {
if len(v.remoteProviders) == 0 {
return RemoteConfigError("No Remote Providers")
}
for _, rp := range v.remoteProviders {
respc, _ := RemoteConfig.WatchChannel(rp)
// Todo: Add quit channel
@@ -1930,9 +1930,15 @@ func (v *Viper) watchKeyValueConfigOnChannel() error {
// Retrieve the first found remote configuration.
func (v *Viper) watchKeyValueConfig() error {
if len(v.remoteProviders) == 0 {
return RemoteConfigError("No Remote Providers")
}
for _, rp := range v.remoteProviders {
val, err := v.watchRemoteConfig(rp)
if err != nil {
v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error())
continue
}
v.kvstore = val
@@ -1975,9 +1981,10 @@ func (v *Viper) AllKeys() []string {
// flattenAndMergeMap recursively flattens the given map into a map[string]bool
// of key paths (used as a set, easier to manipulate than a []string):
// - each path is merged into a single key string, delimited with v.keyDelim
// - if a path is shadowed by an earlier value in the initial shadow map,
// it is skipped.
// - each path is merged into a single key string, delimited with v.keyDelim
// - if a path is shadowed by an earlier value in the initial shadow map,
// it is skipped.
//
// The resulting set of paths is merged to the given shadow set at the same time.
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
if shadow != nil && prefix != "" && shadow[prefix] {
@@ -2128,14 +2135,17 @@ func (v *Viper) getConfigFile() (string, error) {
// Debug prints all configuration registries for debugging
// purposes.
func Debug() { v.Debug() }
func Debug() { v.Debug() }
func DebugTo(w io.Writer) { v.DebugTo(w) }
func (v *Viper) Debug() {
fmt.Printf("Aliases:\n%#v\n", v.aliases)
fmt.Printf("Override:\n%#v\n", v.override)
fmt.Printf("PFlags:\n%#v\n", v.pflags)
fmt.Printf("Env:\n%#v\n", v.env)
fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore)
fmt.Printf("Config:\n%#v\n", v.config)
fmt.Printf("Defaults:\n%#v\n", v.defaults)
func (v *Viper) Debug() { v.DebugTo(os.Stdout) }
func (v *Viper) DebugTo(w io.Writer) {
fmt.Fprintf(w, "Aliases:\n%#v\n", v.aliases)
fmt.Fprintf(w, "Override:\n%#v\n", v.override)
fmt.Fprintf(w, "PFlags:\n%#v\n", v.pflags)
fmt.Fprintf(w, "Env:\n%#v\n", v.env)
fmt.Fprintf(w, "Key/Value Store:\n%#v\n", v.kvstore)
fmt.Fprintf(w, "Config:\n%#v\n", v.config)
fmt.Fprintf(w, "Defaults:\n%#v\n", v.defaults)
}