mirror of
https://github.com/spf13/viper.git
synced 2026-09-20 03:18:21 +00:00
Compare commits
32
Commits
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: "1.22"
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: "1.22"
|
||||
|
||||
@@ -112,4 +112,4 @@ jobs:
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3
|
||||
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
|
||||
uses: github/codeql-action/init@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
# 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@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
|
||||
uses: github/codeql-action/autobuild@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -68,5 +68,5 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
|
||||
uses: github/codeql-action/analyze@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13
|
||||
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
package viper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/viper/internal/encoding/dotenv"
|
||||
"github.com/spf13/viper/internal/encoding/json"
|
||||
"github.com/spf13/viper/internal/encoding/toml"
|
||||
"github.com/spf13/viper/internal/encoding/yaml"
|
||||
)
|
||||
|
||||
// Encoder encodes Viper's internal data structures into a byte representation.
|
||||
// It's primarily used for encoding a map[string]any into a file format.
|
||||
type Encoder interface {
|
||||
Encode(v map[string]any) ([]byte, error)
|
||||
}
|
||||
|
||||
// Decoder decodes the contents of a byte slice into Viper's internal data structures.
|
||||
// It's primarily used for decoding contents of a file into a map[string]any.
|
||||
type Decoder interface {
|
||||
Decode(b []byte, v map[string]any) error
|
||||
}
|
||||
|
||||
// Codec combines [Encoder] and [Decoder] interfaces.
|
||||
type Codec interface {
|
||||
Encoder
|
||||
Decoder
|
||||
}
|
||||
|
||||
// TODO: consider adding specific errors for not found scenarios
|
||||
|
||||
// EncoderRegistry returns an [Encoder] for a given format.
|
||||
//
|
||||
// Format is case-insensitive.
|
||||
//
|
||||
// [EncoderRegistry] returns an error if no [Encoder] is registered for the format.
|
||||
type EncoderRegistry interface {
|
||||
Encoder(format string) (Encoder, error)
|
||||
}
|
||||
|
||||
// DecoderRegistry returns an [Decoder] for a given format.
|
||||
//
|
||||
// Format is case-insensitive.
|
||||
//
|
||||
// [DecoderRegistry] returns an error if no [Decoder] is registered for the format.
|
||||
type DecoderRegistry interface {
|
||||
Decoder(format string) (Decoder, error)
|
||||
}
|
||||
|
||||
// [CodecRegistry] combines [EncoderRegistry] and [DecoderRegistry] interfaces.
|
||||
type CodecRegistry interface {
|
||||
EncoderRegistry
|
||||
DecoderRegistry
|
||||
}
|
||||
|
||||
// WithEncoderRegistry sets a custom [EncoderRegistry].
|
||||
func WithEncoderRegistry(r EncoderRegistry) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.encoderRegistry = r
|
||||
})
|
||||
}
|
||||
|
||||
// WithDecoderRegistry sets a custom [DecoderRegistry].
|
||||
func WithDecoderRegistry(r DecoderRegistry) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.decoderRegistry = r
|
||||
})
|
||||
}
|
||||
|
||||
// WithCodecRegistry sets a custom [EncoderRegistry] and [DecoderRegistry].
|
||||
func WithCodecRegistry(r CodecRegistry) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.encoderRegistry = r
|
||||
v.decoderRegistry = r
|
||||
})
|
||||
}
|
||||
|
||||
// DefaultCodecRegistry is a simple implementation of [CodecRegistry] that allows registering custom [Codec]s.
|
||||
type DefaultCodecRegistry struct {
|
||||
codecs map[string]Codec
|
||||
|
||||
mu sync.RWMutex
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// NewCodecRegistry returns a new [CodecRegistry], ready to accept custom [Codec]s.
|
||||
func NewCodecRegistry() *DefaultCodecRegistry {
|
||||
r := &DefaultCodecRegistry{}
|
||||
|
||||
r.init()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *DefaultCodecRegistry) init() {
|
||||
r.once.Do(func() {
|
||||
r.codecs = map[string]Codec{}
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterCodec registers a custom [Codec].
|
||||
//
|
||||
// Format is case-insensitive.
|
||||
func (r *DefaultCodecRegistry) RegisterCodec(format string, codec Codec) error {
|
||||
r.init()
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
r.codecs[strings.ToLower(format)] = codec
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encoder implements the [EncoderRegistry] interface.
|
||||
//
|
||||
// Format is case-insensitive.
|
||||
func (r *DefaultCodecRegistry) Encoder(format string) (Encoder, error) {
|
||||
encoder, ok := r.codec(format)
|
||||
if !ok {
|
||||
return nil, errors.New("encoder not found for this format")
|
||||
}
|
||||
|
||||
return encoder, nil
|
||||
}
|
||||
|
||||
// Decoder implements the [DecoderRegistry] interface.
|
||||
//
|
||||
// Format is case-insensitive.
|
||||
func (r *DefaultCodecRegistry) Decoder(format string) (Decoder, error) {
|
||||
decoder, ok := r.codec(format)
|
||||
if !ok {
|
||||
return nil, errors.New("decoder not found for this format")
|
||||
}
|
||||
|
||||
return decoder, nil
|
||||
}
|
||||
|
||||
func (r *DefaultCodecRegistry) codec(format string) (Codec, bool) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
format = strings.ToLower(format)
|
||||
|
||||
if r.codecs != nil {
|
||||
codec, ok := r.codecs[format]
|
||||
if ok {
|
||||
return codec, true
|
||||
}
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "yaml", "yml":
|
||||
return yaml.Codec{}, true
|
||||
|
||||
case "json":
|
||||
return json.Codec{}, true
|
||||
|
||||
case "toml":
|
||||
return toml.Codec{}, true
|
||||
|
||||
case "dotenv", "env":
|
||||
return &dotenv.Codec{}, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package viper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type codec struct{}
|
||||
|
||||
func (codec) Encode(_ map[string]any) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (codec) Decode(_ []byte, _ map[string]any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestDefaultCodecRegistry(t *testing.T) {
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
registry := NewCodecRegistry()
|
||||
|
||||
c := codec{}
|
||||
|
||||
err := registry.RegisterCodec("myformat", c)
|
||||
require.NoError(t, err)
|
||||
|
||||
encoder, err := registry.Encoder("myformat")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, c, encoder)
|
||||
|
||||
decoder, err := registry.Decoder("myformat")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, c, decoder)
|
||||
})
|
||||
|
||||
t.Run("CodecNotFound", func(t *testing.T) {
|
||||
registry := NewCodecRegistry()
|
||||
|
||||
_, err := registry.Encoder("myformat")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = registry.Decoder("myformat")
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("FormatIsCaseInsensitive", func(t *testing.T) {
|
||||
registry := NewCodecRegistry()
|
||||
|
||||
c := codec{}
|
||||
|
||||
err := registry.RegisterCodec("MYFORMAT", c)
|
||||
require.NoError(t, err)
|
||||
|
||||
{
|
||||
encoder, err := registry.Encoder("myformat")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, c, encoder)
|
||||
}
|
||||
|
||||
{
|
||||
encoder, err := registry.Encoder("MYFORMAT")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, c, encoder)
|
||||
}
|
||||
|
||||
{
|
||||
decoder, err := registry.Decoder("myformat")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, c, decoder)
|
||||
}
|
||||
|
||||
{
|
||||
decoder, err := registry.Decoder("MYFORMAT")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, c, decoder)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("OverrideDefault", func(t *testing.T) {
|
||||
registry := NewCodecRegistry()
|
||||
|
||||
c := codec{}
|
||||
|
||||
err := registry.RegisterCodec("yaml", c)
|
||||
require.NoError(t, err)
|
||||
|
||||
encoder, err := registry.Encoder("yaml")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, c, encoder)
|
||||
|
||||
decoder, err := registry.Decoder("yaml")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, c, decoder)
|
||||
})
|
||||
}
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
// WithFinder sets a custom [Finder].
|
||||
func WithFinder(f Finder) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.finder = f
|
||||
})
|
||||
}
|
||||
@@ -34,6 +38,10 @@ func (c *combinedFinder) Find(fsys afero.Fs) ([]string, error) {
|
||||
var errs []error
|
||||
|
||||
for _, finder := range c.finders {
|
||||
if finder == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
r, err := finder.Find(fsys)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
|
||||
@@ -4,33 +4,24 @@ go 1.21
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/go-viper/encoding/dotenv v0.1.0
|
||||
github.com/go-viper/encoding/hcl v0.1.0
|
||||
github.com/go-viper/encoding/ini v0.1.0
|
||||
github.com/go-viper/encoding/javaproperties v0.1.0
|
||||
github.com/go-viper/encoding/toml v0.1.0
|
||||
github.com/go-viper/encoding/yaml v0.1.0
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0
|
||||
github.com/pelletier/go-toml/v2 v2.2.2
|
||||
github.com/sagikazarmark/locafero v0.6.0
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cast v1.6.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/subosito/gotenv v1.6.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -5,30 +5,14 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-viper/encoding/dotenv v0.1.0 h1:XelRj/HPBriYmnXY59h1QLMBnmWC21qFojzr9vdzbZo=
|
||||
github.com/go-viper/encoding/dotenv v0.1.0/go.mod h1:SF1B9MODzZ1OLBEePSrjMkJjVg821fFiYea5AIL8CBc=
|
||||
github.com/go-viper/encoding/hcl v0.1.0 h1:eC0Vo0XLTkWMd0ehSeF4H2ushiKu/lwNAzYnStgTkho=
|
||||
github.com/go-viper/encoding/hcl v0.1.0/go.mod h1:uXPhzJnVyTb45tuW8lqhcUDe7DYvrZFoZs0HU9vXOyg=
|
||||
github.com/go-viper/encoding/ini v0.1.0 h1:zzt54JVGIAFGIPYzqauMgJt7gjh9hVC3S+NzWJPvb9Y=
|
||||
github.com/go-viper/encoding/ini v0.1.0/go.mod h1:Pfi4M2V1eAGJVZ5q6FrkHPhtHED2YgLlXhvgMVrB+YQ=
|
||||
github.com/go-viper/encoding/javaproperties v0.1.0 h1:4pQN/pez/rMy9ITZ++SgLH6VIN3zWzNNuWFHKjrpn6w=
|
||||
github.com/go-viper/encoding/javaproperties v0.1.0/go.mod h1:LGaThjx5J/GFdQRJscxLMQsYt0XKAM7IW9YzsJTv6jw=
|
||||
github.com/go-viper/encoding/toml v0.1.0 h1:Gtd343g0JR9lLeBaudZILjLPYiGmzyE0LSpTf6HsYDU=
|
||||
github.com/go-viper/encoding/toml v0.1.0/go.mod h1:+gJiMxUZ/wslHYy06NBtkotxZ7fbue+36KvdMm2OXTk=
|
||||
github.com/go-viper/encoding/yaml v0.1.0 h1:tILva5u4EUgtJDYhAB7U+aj5zIDZqmq5qvcS8U4bFQE=
|
||||
github.com/go-viper/encoding/yaml v0.1.0/go.mod h1:nsYhB8GzJ0rVF6sA0Xiok0mEuHtmHqmCn5tUuKKAwv0=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
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/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -68,8 +52,6 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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.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=
|
||||
|
||||
@@ -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]any.
|
||||
type Decoder interface {
|
||||
Decode(b []byte, v map[string]any) 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]any) error {
|
||||
e.mu.RLock()
|
||||
decoder, ok := e.decoders[format]
|
||||
e.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return ErrDecoderNotFound
|
||||
}
|
||||
|
||||
return decoder.Decode(b, v)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type decoder struct {
|
||||
v map[string]any
|
||||
}
|
||||
|
||||
func (d decoder) Decode(_ []byte, v map[string]any) 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{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("AlreadyRegistered", func(t *testing.T) {
|
||||
registry := NewDecoderRegistry()
|
||||
|
||||
err := registry.RegisterDecoder("myformat", decoder{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = registry.RegisterDecoder("myformat", decoder{})
|
||||
assert.ErrorIs(t, err, ErrDecoderFormatAlreadyRegistered)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDecoderRegistry_Decode(t *testing.T) {
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
registry := NewDecoderRegistry()
|
||||
decoder := decoder{
|
||||
v: map[string]any{
|
||||
"key": "value",
|
||||
},
|
||||
}
|
||||
|
||||
err := registry.RegisterDecoder("myformat", decoder)
|
||||
require.NoError(t, err)
|
||||
|
||||
v := map[string]any{}
|
||||
|
||||
err = registry.Decode("myformat", []byte("key: value"), v)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, decoder.v, v)
|
||||
})
|
||||
|
||||
t.Run("DecoderNotFound", func(t *testing.T) {
|
||||
registry := NewDecoderRegistry()
|
||||
|
||||
v := map[string]any{}
|
||||
|
||||
err := registry.Decode("myformat", nil, v)
|
||||
assert.ErrorIs(t, err, ErrDecoderNotFound)
|
||||
})
|
||||
}
|
||||
@@ -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]any) ([]byte, error) {
|
||||
flattened := map[string]any{}
|
||||
|
||||
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]any) 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
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package dotenv
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// original form of the data.
|
||||
const original = `# key-value pair
|
||||
KEY=value
|
||||
`
|
||||
|
||||
// encoded form of the data.
|
||||
const encoded = `KEY=value
|
||||
`
|
||||
|
||||
// data is Viper's internal representation.
|
||||
var data = map[string]any{
|
||||
"KEY": "value",
|
||||
}
|
||||
|
||||
func TestCodec_Encode(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
b, err := codec.Encode(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, encoded, string(b))
|
||||
}
|
||||
|
||||
func TestCodec_Decode(t *testing.T) {
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
v := map[string]any{}
|
||||
|
||||
err := codec.Decode([]byte(original), v)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, data, v)
|
||||
})
|
||||
|
||||
t.Run("InvalidData", func(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
v := map[string]any{}
|
||||
|
||||
err := codec.Decode([]byte(`invalid data`), v)
|
||||
require.Error(t, err)
|
||||
|
||||
t.Logf("decoding failed as expected: %s", err)
|
||||
})
|
||||
}
|
||||
@@ -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 the main package.
|
||||
// TODO: move it to a common place.
|
||||
func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any {
|
||||
if shadow != nil && prefix != "" && shadow[prefix] != nil {
|
||||
// prefix is shadowed => nothing more to flatten
|
||||
return shadow
|
||||
}
|
||||
if shadow == nil {
|
||||
shadow = make(map[string]any)
|
||||
}
|
||||
|
||||
var m2 map[string]any
|
||||
if prefix != "" {
|
||||
prefix += delimiter
|
||||
}
|
||||
for k, val := range m {
|
||||
fullKey := prefix + k
|
||||
switch val := val.(type) {
|
||||
case map[string]any:
|
||||
m2 = val
|
||||
case map[any]any:
|
||||
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
|
||||
}
|
||||
@@ -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]any into a file format.
|
||||
type Encoder interface {
|
||||
Encode(v map[string]any) ([]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]any) ([]byte, error) {
|
||||
e.mu.RLock()
|
||||
encoder, ok := e.encoders[format]
|
||||
e.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, ErrEncoderNotFound
|
||||
}
|
||||
|
||||
return encoder.Encode(v)
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type encoder struct {
|
||||
b []byte
|
||||
}
|
||||
|
||||
func (e encoder) Encode(_ map[string]any) ([]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{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("AlreadyRegistered", func(t *testing.T) {
|
||||
registry := NewEncoderRegistry()
|
||||
|
||||
err := registry.RegisterEncoder("myformat", encoder{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = registry.RegisterEncoder("myformat", encoder{})
|
||||
assert.ErrorIs(t, err, ErrEncoderFormatAlreadyRegistered)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := registry.Encode("myformat", map[string]any{"key": "value"})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "key: value", string(b))
|
||||
})
|
||||
|
||||
t.Run("EncoderNotFound", func(t *testing.T) {
|
||||
registry := NewEncoderRegistry()
|
||||
|
||||
_, err := registry.Encode("myformat", map[string]any{"key": "value"})
|
||||
assert.ErrorIs(t, err, ErrEncoderNotFound)
|
||||
})
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package encoding
|
||||
|
||||
type encodingError string
|
||||
|
||||
func (e encodingError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
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]any) ([]byte, error) {
|
||||
return toml.Marshal(v)
|
||||
}
|
||||
|
||||
func (Codec) Decode(b []byte, v map[string]any) error {
|
||||
return toml.Unmarshal(b, &v)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// 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']
|
||||
`
|
||||
|
||||
// data is Viper's internal representation.
|
||||
var data = map[string]any{
|
||||
"key": "value",
|
||||
"list": []any{
|
||||
"item1",
|
||||
"item2",
|
||||
"item3",
|
||||
},
|
||||
"map": map[string]any{
|
||||
"key": "value",
|
||||
},
|
||||
"nested_map": map[string]any{
|
||||
"map": map[string]any{
|
||||
"key": "value",
|
||||
"list": []any{
|
||||
"item1",
|
||||
"item2",
|
||||
"item3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestCodec_Encode(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
b, err := codec.Encode(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, encoded, string(b))
|
||||
}
|
||||
|
||||
func TestCodec_Decode(t *testing.T) {
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
v := map[string]any{}
|
||||
|
||||
err := codec.Decode([]byte(original), v)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, data, v)
|
||||
})
|
||||
|
||||
t.Run("InvalidData", func(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
v := map[string]any{}
|
||||
|
||||
err := codec.Decode([]byte(`invalid data`), v)
|
||||
require.Error(t, err)
|
||||
|
||||
t.Logf("decoding failed as expected: %s", err)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package yaml
|
||||
|
||||
import "gopkg.in/yaml.v3"
|
||||
|
||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
|
||||
type Codec struct{}
|
||||
|
||||
func (Codec) Encode(v map[string]any) ([]byte, error) {
|
||||
return yaml.Marshal(v)
|
||||
}
|
||||
|
||||
func (Codec) Decode(b []byte, v map[string]any) error {
|
||||
return yaml.Unmarshal(b, &v)
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// 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
|
||||
// (e.g. map is decoded into a map with interface key).
|
||||
var decoded = map[string]any{
|
||||
"key": "value",
|
||||
"list": []any{
|
||||
"item1",
|
||||
"item2",
|
||||
"item3",
|
||||
},
|
||||
"map": map[string]any{
|
||||
"key": "value",
|
||||
},
|
||||
"nested_map": map[string]any{
|
||||
"map": map[string]any{
|
||||
"key": "value",
|
||||
"list": []any{
|
||||
"item1",
|
||||
"item2",
|
||||
"item3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// data is Viper's internal representation.
|
||||
var data = map[string]any{
|
||||
"key": "value",
|
||||
"list": []any{
|
||||
"item1",
|
||||
"item2",
|
||||
"item3",
|
||||
},
|
||||
"map": map[string]any{
|
||||
"key": "value",
|
||||
},
|
||||
"nested_map": map[string]any{
|
||||
"map": map[string]any{
|
||||
"key": "value",
|
||||
"list": []any{
|
||||
"item1",
|
||||
"item2",
|
||||
"item3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestCodec_Encode(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
b, err := codec.Encode(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, encoded, string(b))
|
||||
}
|
||||
|
||||
func TestCodec_Decode(t *testing.T) {
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
v := map[string]any{}
|
||||
|
||||
err := codec.Decode([]byte(original), v)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, decoded, v)
|
||||
})
|
||||
|
||||
t.Run("InvalidData", func(t *testing.T) {
|
||||
codec := Codec{}
|
||||
|
||||
v := map[string]any{}
|
||||
|
||||
err := codec.Decode([]byte(`invalid data`), v)
|
||||
require.Error(t, err)
|
||||
|
||||
t.Logf("decoding failed as expected: %s", err)
|
||||
})
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// SupportedRemoteProviders are universally supported remote providers.
|
||||
@@ -93,7 +94,7 @@ func AddRemoteProvider(provider, endpoint, path string) error {
|
||||
}
|
||||
|
||||
func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
|
||||
if !stringInSlice(provider, SupportedRemoteProviders) {
|
||||
if !slices.Contains(SupportedRemoteProviders, provider) {
|
||||
return UnsupportedRemoteProviderError(provider)
|
||||
}
|
||||
if provider != "" && endpoint != "" {
|
||||
@@ -126,7 +127,7 @@ func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) err
|
||||
}
|
||||
|
||||
func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
|
||||
if !stringInSlice(provider, SupportedRemoteProviders) {
|
||||
if !slices.Contains(SupportedRemoteProviders, provider) {
|
||||
return UnsupportedRemoteProviderError(provider)
|
||||
}
|
||||
if provider != "" && endpoint != "" {
|
||||
|
||||
+22
-25
@@ -5,24 +5,24 @@ go 1.21
|
||||
replace github.com/spf13/viper => ../
|
||||
|
||||
require (
|
||||
github.com/sagikazarmark/crypt v0.21.0
|
||||
github.com/spf13/viper v1.20.0-alpha.2
|
||||
github.com/sagikazarmark/crypt v0.24.0
|
||||
github.com/spf13/viper v1.20.0-alpha.5
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.115.0 // indirect
|
||||
cloud.google.com/go/auth v0.5.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
cloud.google.com/go/auth v0.7.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||
cloud.google.com/go/firestore v1.15.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.5.7 // indirect
|
||||
cloud.google.com/go/longrunning v0.5.9 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
@@ -30,8 +30,8 @@ require (
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.4 // indirect
|
||||
github.com/hashicorp/consul/api v1.29.1 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
||||
github.com/hashicorp/consul/api v1.29.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
@@ -39,18 +39,16 @@ require (
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.2 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nats-io/nats.go v1.35.0 // indirect
|
||||
github.com/nats-io/nats.go v1.36.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
@@ -61,10 +59,10 @@ require (
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.14 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.14 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.14 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.15 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.15 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.15 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
@@ -74,20 +72,19 @@ require (
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/api v0.185.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240617180043-68d350f18fd4 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 // indirect
|
||||
google.golang.org/grpc v1.64.0 // indirect
|
||||
google.golang.org/api v0.189.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect
|
||||
google.golang.org/grpc v1.65.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
+44
-50
@@ -1,16 +1,16 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
||||
cloud.google.com/go/auth v0.5.1 h1:0QNO7VThG54LUzKiQxv8C6x1YX7lUrzlAa1nVLF8CIw=
|
||||
cloud.google.com/go/auth v0.5.1/go.mod h1:vbZT8GjzDf3AVqCcQmqeeM32U9HBFc32vVVAbwDsa6s=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE=
|
||||
cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8=
|
||||
cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk=
|
||||
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
|
||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
||||
cloud.google.com/go/longrunning v0.5.9 h1:haH9pAuXdPAMqHvzX0zlWQigXT7B0+CL4/2nXXdBo5k=
|
||||
cloud.google.com/go/longrunning v0.5.9/go.mod h1:HD+0l9/OOW0za6UWdKJtXoFAX/BGg/3Wj8p10NeWF7c=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -63,8 +63,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@@ -108,12 +108,12 @@ github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
|
||||
github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
|
||||
github.com/hashicorp/consul/api v1.29.1 h1:UEwOjYJrd3lG1x5w7HxDRMGiAUPrb3f103EoeKuuEcc=
|
||||
github.com/hashicorp/consul/api v1.29.1/go.mod h1:lumfRkY/coLuqMICkI7Fh3ylMG31mQSRZyef2c5YvJI=
|
||||
github.com/hashicorp/consul/proto-public v0.6.1 h1:+uzH3olCrksXYWAYHKqK782CtK9scfqH+Unlw3UHhCg=
|
||||
github.com/hashicorp/consul/proto-public v0.6.1/go.mod h1:cXXbOg74KBNGajC+o8RlA502Esf0R9prcoJgiOX/2Tg=
|
||||
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
|
||||
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
|
||||
github.com/hashicorp/consul/api v1.29.2 h1:aYyRn8EdE2mSfG14S1+L9Qkjtz8RzmaWh6AcNGRNwPw=
|
||||
github.com/hashicorp/consul/api v1.29.2/go.mod h1:0YObcaLNDSbtlgzIRtmRXI1ZkeuK0trCBxwZQ4MYnIk=
|
||||
github.com/hashicorp/consul/proto-public v0.6.2 h1:+DA/3g/IiKlJZb88NBn0ZgXrxJp2NlvCZdEyl+qxvL0=
|
||||
github.com/hashicorp/consul/proto-public v0.6.2/go.mod h1:cXXbOg74KBNGajC+o8RlA502Esf0R9prcoJgiOX/2Tg=
|
||||
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
|
||||
github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -150,8 +150,6 @@ github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
|
||||
@@ -176,8 +174,6 @@ 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/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
@@ -211,8 +207,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/nats.go v1.35.0 h1:XFNqNM7v5B+MQMKqVGAyHwYhyKb48jrenXNxIU20ULk=
|
||||
github.com/nats-io/nats.go v1.35.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
|
||||
github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
@@ -246,8 +242,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sagikazarmark/crypt v0.21.0 h1:nxEbd+foHo2JD6tERQRRbAUk7qDwj6655D6L36hgLBM=
|
||||
github.com/sagikazarmark/crypt v0.21.0/go.mod h1:ZxYvIH6j5KZo35787zYs5wtMUm1bRCDJm6AVqDAfStk=
|
||||
github.com/sagikazarmark/crypt v0.24.0 h1:v/RbcfZT1U6CfGXV+I1WUtWUgo3ewpoSBHyUT6qIGfY=
|
||||
github.com/sagikazarmark/crypt v0.24.0/go.mod h1:RNCCVzIbELuCbLqhzOubaxqLiWnijPEVKWe5UBtEsaQ=
|
||||
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
||||
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
@@ -285,14 +281,14 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqri
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI=
|
||||
go.etcd.io/etcd/client/v2 v2.305.14 h1:v5ASLyFuMlVd/gKU6uf6Cod+vSWKa4Rsv9+eghl0Nwk=
|
||||
go.etcd.io/etcd/client/v2 v2.305.14/go.mod h1:AWYT0lLEkBuqVaGw0UVMtA4rxCb3/oGE8PxZ8cUS4tI=
|
||||
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
|
||||
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
|
||||
go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
|
||||
go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
|
||||
go.etcd.io/etcd/client/v2 v2.305.15 h1:VG2xbf8Vz1KJh65Ar2V5eDmfkp1bpzkSEHlhJM3usp8=
|
||||
go.etcd.io/etcd/client/v2 v2.305.15/go.mod h1:Ad5dRjPVb/n5yXgAWQ/hXzuXXkBk0Y658ocuXYaUU48=
|
||||
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
||||
@@ -322,8 +318,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||
@@ -349,8 +345,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
@@ -388,8 +384,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
@@ -413,26 +409,26 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
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.185.0 h1:ENEKk1k4jW8SmmaT6RE+ZasxmxezCrD5Vw4npvr+pAU=
|
||||
google.golang.org/api v0.185.0/go.mod h1:HNfvIkJGlgrIlrbYkAm9W9IdkmKZjOTVh33YltygGbg=
|
||||
google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI=
|
||||
google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8=
|
||||
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240617180043-68d350f18fd4 h1:CUiCqkPw1nNrNQzCCG4WA65m0nAmQiwXHpub3dNyruU=
|
||||
google.golang.org/genproto v0.0.0-20240617180043-68d350f18fd4/go.mod h1:EvuUDCulqGgV80RvP1BHuom+smhX4qtlhnNatHuroGQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 h1:Di6ANFilr+S60a4S61ZM00vLdw0IrQOSMS2/6mrnOU0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
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=
|
||||
@@ -449,8 +445,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -128,15 +128,6 @@ func absPathify(logger *slog.Logger, inPath string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func userHomeDir() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
||||
|
||||
@@ -29,25 +29,18 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/go-viper/encoding/dotenv"
|
||||
"github.com/go-viper/encoding/hcl"
|
||||
"github.com/go-viper/encoding/ini"
|
||||
"github.com/go-viper/encoding/javaproperties"
|
||||
"github.com/go-viper/encoding/toml"
|
||||
"github.com/go-viper/encoding/yaml"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/spf13/viper/internal/encoding"
|
||||
"github.com/spf13/viper/internal/encoding/json"
|
||||
"github.com/spf13/viper/internal/features"
|
||||
)
|
||||
|
||||
@@ -170,9 +163,6 @@ type Viper struct {
|
||||
configPermissions os.FileMode
|
||||
envPrefix string
|
||||
|
||||
// Specific commands for ini parsing
|
||||
iniLoadOptions ini.LoadOptions
|
||||
|
||||
automaticEnvApplied bool
|
||||
envKeyReplacer StringReplacer
|
||||
allowEmptyEnv bool
|
||||
@@ -191,9 +181,10 @@ type Viper struct {
|
||||
|
||||
logger *slog.Logger
|
||||
|
||||
// TODO: should probably be protected with a mutex
|
||||
encoderRegistry *encoding.EncoderRegistry
|
||||
decoderRegistry *encoding.DecoderRegistry
|
||||
encoderRegistry EncoderRegistry
|
||||
decoderRegistry DecoderRegistry
|
||||
|
||||
decodeHook mapstructure.DecodeHookFunc
|
||||
|
||||
experimentalFinder bool
|
||||
experimentalBindStruct bool
|
||||
@@ -217,7 +208,10 @@ func New() *Viper {
|
||||
v.typeByDefValue = false
|
||||
v.logger = slog.New(&discardHandler{})
|
||||
|
||||
v.resetEncoding()
|
||||
codecRegistry := NewCodecRegistry()
|
||||
|
||||
v.encoderRegistry = codecRegistry
|
||||
v.decoderRegistry = codecRegistry
|
||||
|
||||
v.experimentalFinder = features.Finder
|
||||
v.experimentalBindStruct = features.BindStruct
|
||||
@@ -256,10 +250,25 @@ type StringReplacer interface {
|
||||
// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys.
|
||||
func EnvKeyReplacer(r StringReplacer) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.envKeyReplacer = r
|
||||
})
|
||||
}
|
||||
|
||||
// WithDecodeHook sets a default decode hook for mapstructure.
|
||||
func WithDecodeHook(h mapstructure.DecodeHookFunc) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
if h == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.decodeHook = h
|
||||
})
|
||||
}
|
||||
|
||||
// NewWithOptions creates a new Viper instance.
|
||||
func NewWithOptions(opts ...Option) *Viper {
|
||||
v := New()
|
||||
@@ -268,8 +277,6 @@ func NewWithOptions(opts ...Option) *Viper {
|
||||
opt.apply(v)
|
||||
}
|
||||
|
||||
v.resetEncoding()
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -278,16 +285,9 @@ func NewWithOptions(opts ...Option) *Viper {
|
||||
// Be careful when using this function: subsequent calls may override options you set.
|
||||
// It's always better to use a local Viper instance.
|
||||
func SetOptions(opts ...Option) {
|
||||
keyDelim := v.keyDelim
|
||||
|
||||
for _, opt := range opts {
|
||||
opt.apply(v)
|
||||
}
|
||||
|
||||
// reset encoding if key delimiter changed
|
||||
if keyDelim != v.keyDelim {
|
||||
v.resetEncoding()
|
||||
}
|
||||
}
|
||||
|
||||
// Reset is intended for testing, will reset all to default settings.
|
||||
@@ -300,84 +300,6 @@ func Reset() {
|
||||
resetRemote()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// SupportedExts are universally supported extensions.
|
||||
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
|
||||
|
||||
@@ -539,7 +461,7 @@ func (v *Viper) AddConfigPath(in string) {
|
||||
absin := absPathify(v.logger, in)
|
||||
|
||||
v.logger.Info("adding path to search paths", "path", absin)
|
||||
if !stringInSlice(absin, v.configPaths) {
|
||||
if !slices.Contains(v.configPaths, absin) {
|
||||
v.configPaths = append(v.configPaths, absin)
|
||||
}
|
||||
}
|
||||
@@ -852,6 +774,7 @@ func (v *Viper) Sub(key string) *Viper {
|
||||
subv.automaticEnvApplied = v.automaticEnvApplied
|
||||
subv.envPrefix = v.envPrefix
|
||||
subv.envKeyReplacer = v.envKeyReplacer
|
||||
subv.keyDelim = v.keyDelim
|
||||
subv.config = cast.ToStringMap(data)
|
||||
return subv
|
||||
}
|
||||
@@ -992,7 +915,7 @@ func UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {
|
||||
}
|
||||
|
||||
func (v *Viper) UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {
|
||||
return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
|
||||
return decode(v.Get(key), v.defaultDecoderConfig(rawVal, opts...))
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals the config into a Struct. Make sure that the tags
|
||||
@@ -1015,13 +938,13 @@ func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
|
||||
}
|
||||
|
||||
// TODO: struct keys should be enough?
|
||||
return decode(v.getSettings(keys), defaultDecoderConfig(rawVal, opts...))
|
||||
return decode(v.getSettings(keys), v.defaultDecoderConfig(rawVal, opts...))
|
||||
}
|
||||
|
||||
func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]string, error) {
|
||||
var structKeyMap map[string]any
|
||||
|
||||
err := decode(input, defaultDecoderConfig(&structKeyMap, opts...))
|
||||
err := decode(input, v.defaultDecoderConfig(&structKeyMap, opts...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1038,20 +961,29 @@ func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]stri
|
||||
|
||||
// defaultDecoderConfig returns default mapstructure.DecoderConfig with support
|
||||
// of time.Duration values & string slices.
|
||||
func defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
|
||||
c := &mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: output,
|
||||
WeaklyTypedInput: true,
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
||||
func (v *Viper) defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
|
||||
decodeHook := v.decodeHook
|
||||
if decodeHook == nil {
|
||||
decodeHook = mapstructure.ComposeDecodeHookFunc(
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
// mapstructure.StringToSliceHookFunc(","),
|
||||
stringToWeakSliceHookFunc(","),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
c := &mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
WeaklyTypedInput: true,
|
||||
DecodeHook: decodeHook,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
|
||||
// Do not allow overwriting the output
|
||||
c.Result = output
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -1093,7 +1025,7 @@ func UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
|
||||
}
|
||||
|
||||
func (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
|
||||
config := defaultDecoderConfig(rawVal, opts...)
|
||||
config := v.defaultDecoderConfig(rawVal, opts...)
|
||||
config.ErrorUnused = true
|
||||
|
||||
keys := v.AllKeys()
|
||||
@@ -1548,7 +1480,7 @@ func (v *Viper) ReadInConfig() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !stringInSlice(v.getConfigType(), SupportedExts) {
|
||||
if !slices.Contains(SupportedExts, v.getConfigType()) {
|
||||
return UnsupportedConfigError(v.getConfigType())
|
||||
}
|
||||
|
||||
@@ -1579,7 +1511,7 @@ func (v *Viper) MergeInConfig() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !stringInSlice(v.getConfigType(), SupportedExts) {
|
||||
if !slices.Contains(SupportedExts, v.getConfigType()) {
|
||||
return UnsupportedConfigError(v.getConfigType())
|
||||
}
|
||||
|
||||
@@ -1660,6 +1592,19 @@ func (v *Viper) WriteConfigAs(filename string) error {
|
||||
return v.writeConfig(filename, true)
|
||||
}
|
||||
|
||||
// WriteConfigTo writes current configuration to an [io.Writer].
|
||||
func WriteConfigTo(w io.Writer) error { return v.WriteConfigTo(w) }
|
||||
|
||||
func (v *Viper) WriteConfigTo(w io.Writer) error {
|
||||
format := strings.ToLower(v.getConfigType())
|
||||
|
||||
if !slices.Contains(SupportedExts, format) {
|
||||
return UnsupportedConfigError(format)
|
||||
}
|
||||
|
||||
return v.marshalWriter(w, format)
|
||||
}
|
||||
|
||||
// SafeWriteConfigAs writes current configuration to a given filename if it does not exist.
|
||||
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }
|
||||
|
||||
@@ -1686,7 +1631,7 @@ func (v *Viper) writeConfig(filename string, force bool) error {
|
||||
return fmt.Errorf("config type could not be determined for %s", filename)
|
||||
}
|
||||
|
||||
if !stringInSlice(configType, SupportedExts) {
|
||||
if !slices.Contains(SupportedExts, configType) {
|
||||
return UnsupportedConfigError(configType)
|
||||
}
|
||||
if v.config == nil {
|
||||
@@ -1713,12 +1658,20 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(in)
|
||||
|
||||
switch format := strings.ToLower(v.getConfigType()); format {
|
||||
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}
|
||||
}
|
||||
format := strings.ToLower(v.getConfigType())
|
||||
|
||||
if !slices.Contains(SupportedExts, format) {
|
||||
return UnsupportedConfigError(format)
|
||||
}
|
||||
|
||||
decoder, err := v.decoderRegistry.Decoder(format)
|
||||
if err != nil {
|
||||
return ConfigParseError{err}
|
||||
}
|
||||
|
||||
err = decoder.Decode(buf.Bytes(), c)
|
||||
if err != nil {
|
||||
return ConfigParseError{err}
|
||||
}
|
||||
|
||||
insensitiviseMap(c)
|
||||
@@ -1726,20 +1679,24 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {
|
||||
}
|
||||
|
||||
// Marshal a map into Writer.
|
||||
func (v *Viper) marshalWriter(f afero.File, configType string) error {
|
||||
func (v *Viper) marshalWriter(w io.Writer, configType string) error {
|
||||
c := v.AllSettings()
|
||||
switch configType {
|
||||
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}
|
||||
}
|
||||
|
||||
_, err = f.WriteString(string(b))
|
||||
if err != nil {
|
||||
return ConfigMarshalError{err}
|
||||
}
|
||||
encoder, err := v.encoderRegistry.Encoder(configType)
|
||||
if err != nil {
|
||||
return ConfigMarshalError{err}
|
||||
}
|
||||
|
||||
b, err := encoder.Encode(c)
|
||||
if err != nil {
|
||||
return ConfigMarshalError{err}
|
||||
}
|
||||
|
||||
_, err = w.Write(b)
|
||||
if err != nil {
|
||||
return ConfigMarshalError{err}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2019,13 +1976,6 @@ func (v *Viper) SetConfigPermissions(perm os.FileMode) {
|
||||
v.configPermissions = perm.Perm()
|
||||
}
|
||||
|
||||
// IniLoadOptions sets the load options for ini parsing.
|
||||
func IniLoadOptions(in ini.LoadOptions) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
v.iniLoadOptions = in
|
||||
})
|
||||
}
|
||||
|
||||
func (v *Viper) getConfigType() string {
|
||||
if v.configType != "" {
|
||||
return v.configType
|
||||
|
||||
+58
-239
@@ -84,58 +84,12 @@ var jsonExample = []byte(`{
|
||||
}
|
||||
}`)
|
||||
|
||||
var hclExample = []byte(`
|
||||
id = "0001"
|
||||
type = "donut"
|
||||
name = "Cake"
|
||||
ppu = 0.55
|
||||
foos {
|
||||
foo {
|
||||
key = 1
|
||||
}
|
||||
foo {
|
||||
key = 2
|
||||
}
|
||||
foo {
|
||||
key = 3
|
||||
}
|
||||
foo {
|
||||
key = 4
|
||||
}
|
||||
}`)
|
||||
|
||||
var propertiesExample = []byte(`
|
||||
p_id: 0001
|
||||
p_type: donut
|
||||
p_name: Cake
|
||||
p_ppu: 0.55
|
||||
p_batters.batter.type: Regular
|
||||
`)
|
||||
|
||||
var remoteExample = []byte(`{
|
||||
"id":"0002",
|
||||
"type":"cronut",
|
||||
"newkey":"remote"
|
||||
}`)
|
||||
|
||||
var iniExample = []byte(`; Package name
|
||||
NAME = ini
|
||||
; Package version
|
||||
VERSION = v1
|
||||
; Package import path
|
||||
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||
|
||||
# Information about package author
|
||||
# Bio can be written in multiple lines.
|
||||
[author]
|
||||
NAME = Unknown ; Succeeding comment
|
||||
E-MAIL = fake@localhost
|
||||
GITHUB = https://github.com/%(NAME)s
|
||||
BIO = """Gopher.
|
||||
Coding addict.
|
||||
Good man.
|
||||
""" # Succeeding comment`)
|
||||
|
||||
func initConfigs(v *Viper) {
|
||||
var r io.Reader
|
||||
v.SetConfigType("yaml")
|
||||
@@ -146,14 +100,6 @@ func initConfigs(v *Viper) {
|
||||
r = bytes.NewReader(jsonExample)
|
||||
v.unmarshalReader(r, v.config)
|
||||
|
||||
v.SetConfigType("hcl")
|
||||
r = bytes.NewReader(hclExample)
|
||||
v.unmarshalReader(r, v.config)
|
||||
|
||||
v.SetConfigType("properties")
|
||||
r = bytes.NewReader(propertiesExample)
|
||||
v.unmarshalReader(r, v.config)
|
||||
|
||||
v.SetConfigType("toml")
|
||||
r = bytes.NewReader(tomlExample)
|
||||
v.unmarshalReader(r, v.config)
|
||||
@@ -165,10 +111,6 @@ func initConfigs(v *Viper) {
|
||||
v.SetConfigType("json")
|
||||
remote := bytes.NewReader(remoteExample)
|
||||
v.unmarshalReader(remote, v.kvstore)
|
||||
|
||||
v.SetConfigType("ini")
|
||||
r = bytes.NewReader(iniExample)
|
||||
v.unmarshalReader(r, v.config)
|
||||
}
|
||||
|
||||
func initConfig(typ, config string, v *Viper) {
|
||||
@@ -622,22 +564,11 @@ func TestJSON(t *testing.T) {
|
||||
assert.Equal(t, "0001", v.Get("id"))
|
||||
}
|
||||
|
||||
func TestProperties(t *testing.T) {
|
||||
v := New()
|
||||
|
||||
v.SetConfigType("properties")
|
||||
|
||||
// Read the properties data into Viper configuration
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(propertiesExample)), "Error reading properties data")
|
||||
|
||||
assert.Equal(t, "0001", v.Get("p_id"))
|
||||
}
|
||||
|
||||
func TestTOML(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("toml")
|
||||
|
||||
// Read the properties data into Viper configuration
|
||||
// Read the TOML data into Viper configuration
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(tomlExample)), "Error reading toml data")
|
||||
|
||||
assert.Equal(t, "TOML Example", v.Get("title"))
|
||||
@@ -646,42 +577,16 @@ func TestTOML(t *testing.T) {
|
||||
func TestDotEnv(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("env")
|
||||
// Read the properties data into Viper configuration
|
||||
// Read the dotenv data into Viper configuration
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(dotenvExample)), "Error reading env data")
|
||||
|
||||
assert.Equal(t, "DotEnv Example", v.Get("title_dotenv"))
|
||||
}
|
||||
|
||||
func TestHCL(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("hcl")
|
||||
// Read the properties data into Viper configuration
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(hclExample)), "Error reading hcl data")
|
||||
|
||||
// initHcl()
|
||||
assert.Equal(t, "0001", v.Get("id"))
|
||||
assert.Equal(t, 0.55, v.Get("ppu"))
|
||||
assert.Equal(t, "donut", v.Get("type"))
|
||||
assert.Equal(t, "Cake", v.Get("name"))
|
||||
v.Set("id", "0002")
|
||||
assert.Equal(t, "0002", v.Get("id"))
|
||||
assert.NotEqual(t, "cronut", v.Get("type"))
|
||||
}
|
||||
|
||||
func TestIni(t *testing.T) {
|
||||
// initIni()
|
||||
v := New()
|
||||
v.SetConfigType("ini")
|
||||
// Read the properties data into Viper configuration
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(iniExample)), "Error reading ini data")
|
||||
|
||||
assert.Equal(t, "ini", v.Get("default.name"))
|
||||
}
|
||||
|
||||
func TestRemotePrecedence(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("json")
|
||||
// Read the properties data into Viper configuration v.config
|
||||
// Read the remote data into Viper configuration v.config
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), "Error reading json data")
|
||||
|
||||
assert.Equal(t, "0001", v.Get("id"))
|
||||
@@ -701,7 +606,7 @@ func TestRemotePrecedence(t *testing.T) {
|
||||
func TestEnv(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("json")
|
||||
// Read the properties data into Viper configuration v.config
|
||||
// Read the JSON data into Viper configuration v.config
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), "Error reading json data")
|
||||
|
||||
v.BindEnv("id")
|
||||
@@ -724,7 +629,7 @@ func TestEnv(t *testing.T) {
|
||||
func TestMultipleEnv(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("json")
|
||||
// Read the properties data into Viper configuration v.config
|
||||
// Read the JSON data into Viper configuration v.config
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), "Error reading json data")
|
||||
|
||||
v.BindEnv("f", "FOOD", "OLD_FOOD")
|
||||
@@ -737,7 +642,7 @@ func TestMultipleEnv(t *testing.T) {
|
||||
func TestEmptyEnv(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("json")
|
||||
// Read the properties data into Viper configuration v.config
|
||||
// Read the JSON data into Viper configuration v.config
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), "Error reading json data")
|
||||
|
||||
v.BindEnv("type") // Empty environment variable
|
||||
@@ -752,7 +657,7 @@ func TestEmptyEnv(t *testing.T) {
|
||||
func TestEmptyEnv_Allowed(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("json")
|
||||
// Read the properties data into Viper configuration v.config
|
||||
// Read the JSON data into Viper configuration v.config
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), "Error reading json data")
|
||||
|
||||
v.AllowEmptyEnv(true)
|
||||
@@ -769,7 +674,7 @@ func TestEmptyEnv_Allowed(t *testing.T) {
|
||||
func TestEnvPrefix(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("json")
|
||||
// Read the properties data into Viper configuration v.config
|
||||
// Read the JSON data into Viper configuration v.config
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), "Error reading json data")
|
||||
|
||||
v.SetEnvPrefix("foo") // will be uppercased automatically
|
||||
@@ -829,7 +734,7 @@ func TestEnvKeyReplacer(t *testing.T) {
|
||||
func TestEnvSubConfig(t *testing.T) {
|
||||
v := New()
|
||||
v.SetConfigType("yaml")
|
||||
// Read the properties data into Viper configuration v.config
|
||||
// Read the YAML data into Viper configuration v.config
|
||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)), "Error reading json data")
|
||||
v.AutomaticEnv()
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
@@ -851,10 +756,6 @@ func TestAllKeys(t *testing.T) {
|
||||
|
||||
ks := []string{
|
||||
"title",
|
||||
"author.bio",
|
||||
"author.e-mail",
|
||||
"author.github",
|
||||
"author.name",
|
||||
"newkey",
|
||||
"owner.organization",
|
||||
"owner.dob",
|
||||
@@ -866,21 +767,12 @@ func TestAllKeys(t *testing.T) {
|
||||
"hobbies",
|
||||
"clothing.jacket",
|
||||
"clothing.trousers",
|
||||
"default.import_path",
|
||||
"default.name",
|
||||
"default.version",
|
||||
"clothing.pants.size",
|
||||
"age",
|
||||
"hacker",
|
||||
"id",
|
||||
"type",
|
||||
"eyes",
|
||||
"p_id",
|
||||
"p_ppu",
|
||||
"p_batters.batter.type",
|
||||
"p_type",
|
||||
"p_name",
|
||||
"foos",
|
||||
"title_dotenv",
|
||||
"type_dotenv",
|
||||
"name_dotenv",
|
||||
@@ -893,24 +785,13 @@ func TestAllKeys(t *testing.T) {
|
||||
"dob": dob,
|
||||
},
|
||||
"title": "TOML Example",
|
||||
"author": map[string]any{
|
||||
"e-mail": "fake@localhost",
|
||||
"github": "https://github.com/Unknown",
|
||||
"name": "Unknown",
|
||||
"bio": "Gopher.\nCoding addict.\nGood man.\n",
|
||||
},
|
||||
"ppu": 0.55,
|
||||
"eyes": "brown",
|
||||
"ppu": 0.55,
|
||||
"eyes": "brown",
|
||||
"clothing": map[string]any{
|
||||
"trousers": "denim",
|
||||
"jacket": "leather",
|
||||
"pants": map[string]any{"size": "large"},
|
||||
},
|
||||
"default": map[string]any{
|
||||
"import_path": "gopkg.in/ini.v1",
|
||||
"name": "ini",
|
||||
"version": "v1",
|
||||
},
|
||||
"id": "0001",
|
||||
"batters": map[string]any{
|
||||
"batter": []any{
|
||||
@@ -927,27 +808,10 @@ func TestAllKeys(t *testing.T) {
|
||||
"snowboarding",
|
||||
"go",
|
||||
},
|
||||
"age": 35,
|
||||
"type": "donut",
|
||||
"newkey": "remote",
|
||||
"name": "Cake",
|
||||
"p_id": "0001",
|
||||
"p_ppu": "0.55",
|
||||
"p_name": "Cake",
|
||||
"p_batters": map[string]any{
|
||||
"batter": map[string]any{"type": "Regular"},
|
||||
},
|
||||
"p_type": "donut",
|
||||
"foos": []map[string]any{
|
||||
{
|
||||
"foo": []map[string]any{
|
||||
{"key": 1},
|
||||
{"key": 2},
|
||||
{"key": 3},
|
||||
{"key": 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
"age": 35,
|
||||
"type": "donut",
|
||||
"newkey": "remote",
|
||||
"name": "Cake",
|
||||
"title_dotenv": "DotEnv Example",
|
||||
"type_dotenv": "donut",
|
||||
"name_dotenv": "Cake",
|
||||
@@ -1030,6 +894,41 @@ func TestUnmarshal(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestUnmarshalWithDefaultDecodeHook(t *testing.T) {
|
||||
opt := mapstructure.ComposeDecodeHookFunc(
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
// Custom Decode Hook Function
|
||||
func(rf reflect.Kind, rt reflect.Kind, data any) (any, error) {
|
||||
if rf != reflect.String || rt != reflect.Map {
|
||||
return data, nil
|
||||
}
|
||||
m := map[string]string{}
|
||||
raw := data.(string)
|
||||
if raw == "" {
|
||||
return m, nil
|
||||
}
|
||||
err := json.Unmarshal([]byte(raw), &m)
|
||||
return m, err
|
||||
},
|
||||
)
|
||||
|
||||
v := NewWithOptions(WithDecodeHook(opt))
|
||||
v.Set("credentials", "{\"foo\":\"bar\"}")
|
||||
|
||||
type config struct {
|
||||
Credentials map[string]string
|
||||
}
|
||||
|
||||
var C config
|
||||
|
||||
require.NoError(t, v.Unmarshal(&C), "unable to decode into struct")
|
||||
|
||||
assert.Equal(t, &config{
|
||||
Credentials: map[string]string{"foo": "bar"},
|
||||
}, &C)
|
||||
}
|
||||
|
||||
func TestUnmarshalWithDecoderOptions(t *testing.T) {
|
||||
v := New()
|
||||
v.Set("credentials", "{\"foo\":\"bar\"}")
|
||||
@@ -1611,24 +1510,6 @@ func TestFindsNestedKeys(t *testing.T) {
|
||||
"clothing.trousers": "denim",
|
||||
"owner.dob": dob,
|
||||
"beard": true,
|
||||
"foos": []map[string]any{
|
||||
{
|
||||
"foo": []map[string]any{
|
||||
{
|
||||
"key": 1,
|
||||
},
|
||||
{
|
||||
"key": 2,
|
||||
},
|
||||
{
|
||||
"key": 3,
|
||||
},
|
||||
{
|
||||
"key": 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for key, expectedValue := range expected {
|
||||
@@ -1800,32 +1681,17 @@ func TestSub(t *testing.T) {
|
||||
assert.Equal(t, []string{"clothing", "pants"}, subv.parents)
|
||||
}
|
||||
|
||||
var hclWriteExpected = []byte(`"foos" = {
|
||||
"foo" = {
|
||||
"key" = 1
|
||||
}
|
||||
func TestSubWithKeyDelimiter(t *testing.T) {
|
||||
v := NewWithOptions(KeyDelimiter("::"))
|
||||
v.SetConfigType("yaml")
|
||||
r := strings.NewReader(string(yamlExampleWithDot))
|
||||
err := v.unmarshalReader(r, v.config)
|
||||
require.NoError(t, err)
|
||||
|
||||
"foo" = {
|
||||
"key" = 2
|
||||
}
|
||||
|
||||
"foo" = {
|
||||
"key" = 3
|
||||
}
|
||||
|
||||
"foo" = {
|
||||
"key" = 4
|
||||
}
|
||||
subv := v.Sub("emails")
|
||||
assert.Equal(t, "01/02/03", subv.Get("steve@hacker.com::created"))
|
||||
}
|
||||
|
||||
"id" = "0001"
|
||||
|
||||
"name" = "Cake"
|
||||
|
||||
"ppu" = 0.55
|
||||
|
||||
"type" = "donut"`)
|
||||
|
||||
var jsonWriteExpected = []byte(`{
|
||||
"batters": {
|
||||
"batter": [
|
||||
@@ -1849,13 +1715,6 @@ var jsonWriteExpected = []byte(`{
|
||||
"type": "donut"
|
||||
}`)
|
||||
|
||||
var propertiesWriteExpected = []byte(`p_id = 0001
|
||||
p_type = donut
|
||||
p_name = Cake
|
||||
p_ppu = 0.55
|
||||
p_batters.batter.type = Regular
|
||||
`)
|
||||
|
||||
// var yamlWriteExpected = []byte(`age: 35
|
||||
// beard: true
|
||||
// clothing:
|
||||
@@ -1882,30 +1741,6 @@ func TestWriteConfig(t *testing.T) {
|
||||
input []byte
|
||||
expectedContent []byte
|
||||
}{
|
||||
"hcl with file extension": {
|
||||
configName: "c",
|
||||
inConfigType: "hcl",
|
||||
outConfigType: "hcl",
|
||||
fileName: "c.hcl",
|
||||
input: hclExample,
|
||||
expectedContent: hclWriteExpected,
|
||||
},
|
||||
"hcl without file extension": {
|
||||
configName: "c",
|
||||
inConfigType: "hcl",
|
||||
outConfigType: "hcl",
|
||||
fileName: "c",
|
||||
input: hclExample,
|
||||
expectedContent: hclWriteExpected,
|
||||
},
|
||||
"hcl with file extension and mismatch type": {
|
||||
configName: "c",
|
||||
inConfigType: "hcl",
|
||||
outConfigType: "json",
|
||||
fileName: "c.hcl",
|
||||
input: hclExample,
|
||||
expectedContent: hclWriteExpected,
|
||||
},
|
||||
"json with file extension": {
|
||||
configName: "c",
|
||||
inConfigType: "json",
|
||||
@@ -1930,22 +1765,6 @@ func TestWriteConfig(t *testing.T) {
|
||||
input: jsonExample,
|
||||
expectedContent: jsonWriteExpected,
|
||||
},
|
||||
"properties with file extension": {
|
||||
configName: "c",
|
||||
inConfigType: "properties",
|
||||
outConfigType: "properties",
|
||||
fileName: "c.properties",
|
||||
input: propertiesExample,
|
||||
expectedContent: propertiesWriteExpected,
|
||||
},
|
||||
"properties without file extension": {
|
||||
configName: "c",
|
||||
inConfigType: "properties",
|
||||
outConfigType: "properties",
|
||||
fileName: "c",
|
||||
input: propertiesExample,
|
||||
expectedContent: propertiesWriteExpected,
|
||||
},
|
||||
"yaml with file extension": {
|
||||
configName: "c",
|
||||
inConfigType: "yaml",
|
||||
|
||||
Reference in New Issue
Block a user