feat: lazy initialization codec

This commit is contained in:
xieyuschen 2023-08-18 11:27:47 +08:00
parent 097e0d888f
commit 6be49d9472
No known key found for this signature in database
GPG key ID: 3DA8961F0CA3B3D8
15 changed files with 371 additions and 383 deletions

View file

@ -0,0 +1,11 @@
package codec
type Codec interface {
// Decode decodes the contents of b into v.
// It's primarily used for decoding contents of a file into a map[string]interface{}.
Decode(b []byte, v map[string]interface{}) error
// Encode encodes the contents of v into a byte representation.
// It's primarily used for encoding a map[string]interface{} into a file format.
Encode(v map[string]interface{}) ([]byte, error)
}

View file

@ -1,61 +0,0 @@
package encoding
import (
"sync"
)
// Decoder decodes the contents of b into v.
// It's primarily used for decoding contents of a file into a map[string]interface{}.
type Decoder interface {
Decode(b []byte, v map[string]interface{}) error
}
const (
// ErrDecoderNotFound is returned when there is no decoder registered for a format.
ErrDecoderNotFound = encodingError("decoder not found for this format")
// ErrDecoderFormatAlreadyRegistered is returned when an decoder is already registered for a format.
ErrDecoderFormatAlreadyRegistered = encodingError("decoder already registered for this format")
)
// DecoderRegistry can choose an appropriate Decoder based on the provided format.
type DecoderRegistry struct {
decoders map[string]Decoder
mu sync.RWMutex
}
// NewDecoderRegistry returns a new, initialized DecoderRegistry.
func NewDecoderRegistry() *DecoderRegistry {
return &DecoderRegistry{
decoders: make(map[string]Decoder),
}
}
// RegisterDecoder registers a Decoder for a format.
// Registering a Decoder for an already existing format is not supported.
func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
e.mu.Lock()
defer e.mu.Unlock()
if _, ok := e.decoders[format]; ok {
return ErrDecoderFormatAlreadyRegistered
}
e.decoders[format] = enc
return nil
}
// Decode calls the underlying Decoder based on the format.
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
e.mu.RLock()
decoder, ok := e.decoders[format]
e.mu.RUnlock()
if !ok {
return ErrDecoderNotFound
}
return decoder.Decode(b, v)
}

View file

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

View file

@ -6,16 +6,21 @@ import (
"sort"
"strings"
"github.com/spf13/viper/internal/encoding/codec"
"github.com/subosito/gotenv"
)
const keyDelimiter = "_"
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for encoding data containing environment variables
// Codec implements the Codec interface for encoding data containing environment variables
// (commonly called as dotenv format).
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func New(args ...interface{}) codec.Codec {
return &Codec{}
}
func (*Codec) Encode(v map[string]interface{}) ([]byte, error) {
flattened := map[string]interface{}{}
flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter)
@ -40,7 +45,7 @@ func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (*Codec) Decode(b []byte, v map[string]interface{}) error {
var buf bytes.Buffer
_, err := buf.Write(b)

View file

@ -1,60 +0,0 @@
package encoding
import (
"sync"
)
// Encoder encodes the contents of v into a byte representation.
// It's primarily used for encoding a map[string]interface{} into a file format.
type Encoder interface {
Encode(v map[string]interface{}) ([]byte, error)
}
const (
// ErrEncoderNotFound is returned when there is no encoder registered for a format.
ErrEncoderNotFound = encodingError("encoder not found for this format")
// ErrEncoderFormatAlreadyRegistered is returned when an encoder is already registered for a format.
ErrEncoderFormatAlreadyRegistered = encodingError("encoder already registered for this format")
)
// EncoderRegistry can choose an appropriate Encoder based on the provided format.
type EncoderRegistry struct {
encoders map[string]Encoder
mu sync.RWMutex
}
// NewEncoderRegistry returns a new, initialized EncoderRegistry.
func NewEncoderRegistry() *EncoderRegistry {
return &EncoderRegistry{
encoders: make(map[string]Encoder),
}
}
// RegisterEncoder registers an Encoder for a format.
// Registering a Encoder for an already existing format is not supported.
func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error {
e.mu.Lock()
defer e.mu.Unlock()
if _, ok := e.encoders[format]; ok {
return ErrEncoderFormatAlreadyRegistered
}
e.encoders[format] = enc
return nil
}
func (e *EncoderRegistry) Encode(format string, v map[string]interface{}) ([]byte, error) {
e.mu.RLock()
encoder, ok := e.encoders[format]
e.mu.RUnlock()
if !ok {
return nil, ErrEncoderNotFound
}
return encoder.Encode(v)
}

View file

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

View file

@ -4,15 +4,21 @@ import (
"bytes"
"encoding/json"
"github.com/spf13/viper/internal/encoding/codec"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/printer"
)
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for HCL encoding.
// Codec implements the encoding.Codec interface for HCL encoding.
// TODO: add printer config to the codec?
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func New(args ...interface{}) codec.Codec {
return &Codec{}
}
func (*Codec) Encode(v map[string]interface{}) ([]byte, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, err
@ -35,6 +41,6 @@ func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (*Codec) Decode(b []byte, v map[string]interface{}) error {
return hcl.Unmarshal(b, &v)
}

View file

@ -5,6 +5,8 @@ import (
"sort"
"strings"
"github.com/spf13/viper/internal/encoding/codec"
"github.com/spf13/cast"
"gopkg.in/ini.v1"
)
@ -13,13 +15,34 @@ import (
// 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.
// Codec implements the encoding.Codec interface for INI encoding.
type Codec struct {
KeyDelimiter string
LoadOptions LoadOptions
}
func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
// New treats its first argument as string for KeyDelimiter and the second as ini.LoadOptions.
// The other args will be ignored
func New(args ...interface{}) codec.Codec {
if len(args) < 2 {
return nil
}
keyDelimiter, ok := args[0].(string)
if !ok {
return nil
}
loadOptions, ok := args[1].(LoadOptions)
if !ok {
return nil
}
return &Codec{
KeyDelimiter: keyDelimiter,
LoadOptions: loadOptions,
}
}
func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
cfg := ini.Empty()
ini.PrettyFormat = false
@ -62,7 +85,7 @@ func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func (c Codec) Decode(b []byte, v map[string]interface{}) error {
func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
cfg := ini.Empty(c.LoadOptions)
err := cfg.Append(b)
@ -90,7 +113,7 @@ func (c Codec) Decode(b []byte, v map[string]interface{}) error {
return nil
}
func (c Codec) keyDelimiter() string {
func (c *Codec) keyDelimiter() string {
if c.KeyDelimiter == "" {
return "."
}

View file

@ -5,11 +5,13 @@ import (
"sort"
"strings"
"github.com/spf13/viper/internal/encoding/codec"
"github.com/magiconair/properties"
"github.com/spf13/cast"
)
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding.
// Codec implements the encoding.Codec interface for Java properties encoding.
type Codec struct {
KeyDelimiter string
@ -20,6 +22,20 @@ type Codec struct {
Properties *properties.Properties
}
// New treats its first argument as string for KeyDelimiter, the other args will be ignored
func New(args ...interface{}) codec.Codec {
if len(args) == 0 {
return nil
}
keyDelimiter, ok := args[0].(string)
if !ok {
return nil
}
return &Codec{
KeyDelimiter: keyDelimiter,
}
}
func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
if c.Properties == nil {
c.Properties = properties.NewProperties()
@ -77,7 +93,7 @@ func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
return nil
}
func (c Codec) keyDelimiter() string {
func (c *Codec) keyDelimiter() string {
if c.KeyDelimiter == "" {
return "."
}

View file

@ -2,16 +2,22 @@ package json
import (
"encoding/json"
"github.com/spf13/viper/internal/encoding/codec"
)
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
// Codec implements the encoding.Codec interface for JSON encoding.
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func New(_ ...interface{}) codec.Codec {
return &Codec{}
}
func (*Codec) Encode(v map[string]interface{}) ([]byte, error) {
// TODO: expose prefix and indent in the Codec as setting?
return json.MarshalIndent(v, "", " ")
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (*Codec) Decode(b []byte, v map[string]interface{}) error {
return json.Unmarshal(b, &v)
}

View file

@ -0,0 +1,115 @@
package encoding
import (
"sync"
"github.com/spf13/viper/internal/encoding/codec"
"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"
)
const (
// ErrCodecNotFound is returned when there is no codec registered for a format.
ErrCodecNotFound = encodingError("codec not found for this format")
// ErrCodecFormatAlreadyRegistered is returned when a codec is already registered for a format.
ErrCodecFormatAlreadyRegistered = encodingError("codec already registered for this format")
)
// supportedCodecFormats stores all supported codec, the empty pointers are used to construct a corresponding
// codec object without reflection.
var supportedCodecFormats = map[string]func(args ...interface{}) codec.Codec{
"yaml": yaml.New,
"yml": yaml.New,
"json": json.New,
"toml": toml.New,
"hcl": hcl.New,
"tfvars": hcl.New,
"ini": ini.New,
"properties": javaproperties.New,
"props": javaproperties.New,
"prop": javaproperties.New,
"dotenv": dotenv.New,
"env": dotenv.New,
}
type CodecRegistry struct {
codecs map[string]codec.Codec
mu sync.RWMutex
keyDelim string
iniLoadOptions ini.LoadOptions
}
// NewCodecRegistry returns a new, initialized CodecRegistry.
func NewCodecRegistry(keyDelim string, iniLoadOptions ini.LoadOptions) *CodecRegistry {
return &CodecRegistry{
codecs: make(map[string]codec.Codec),
keyDelim: keyDelim,
iniLoadOptions: iniLoadOptions,
}
}
func (e *CodecRegistry) getCodecLazily(format string) (codec.Codec, error) {
e.mu.RLock()
c, ok := e.codecs[format]
e.mu.RUnlock()
if ok {
return c, nil
}
newCodecFn, ok := supportedCodecFormats[format]
if !ok {
return nil, ErrCodecNotFound
}
switch format {
case "ini":
c = newCodecFn(e.keyDelim, e.iniLoadOptions)
case "properties", "props", "prop":
c = newCodecFn(e.keyDelim)
default:
c = newCodecFn()
}
e.mu.Lock()
defer e.mu.Unlock()
e.codecs[format] = c
return c, nil
}
func (e *CodecRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
decoder, err := e.getCodecLazily(format)
if err != nil {
return err
}
return decoder.Decode(b, v)
}
func (e *CodecRegistry) Encode(format string, v map[string]interface{}) ([]byte, error) {
decoder, err := e.getCodecLazily(format)
if err != nil {
return nil, err
}
return decoder.Encode(v)
}
// RegisterCodec registers a Codec for a format.
// Registering a Codec for an already existing format is not supported.
func (e *CodecRegistry) RegisterCodec(format string, codec codec.Codec) error {
e.mu.Lock()
defer e.mu.Unlock()
if _, ok := e.codecs[format]; ok {
return ErrCodecFormatAlreadyRegistered
}
e.codecs[format] = codec
return nil
}

View file

@ -0,0 +1,125 @@
package encoding
import (
"reflect"
"testing"
"github.com/spf13/viper/internal/encoding/constructor"
"github.com/spf13/viper/internal/encoding/ini"
)
type codec struct {
v map[string]interface{}
b []byte
}
func (c *codec) Construct() constructor.Codec {
return &codec{}
}
func (c *codec) Encode(_ map[string]interface{}) ([]byte, error) {
return c.b, nil
}
func (c *codec) Decode(_ []byte, v map[string]interface{}) error {
for key, value := range c.v {
v[key] = value
}
return nil
}
func TestCodecRegistry_RegisterCodec(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewCodecRegistry("", ini.LoadOptions{})
err := registry.RegisterCodec("myformat", &codec{})
if err != nil {
t.Fatal(err)
}
})
t.Run("AlreadyRegistered", func(t *testing.T) {
registry := NewCodecRegistry("", ini.LoadOptions{})
err := registry.RegisterCodec("myformat", &codec{})
if err != nil {
t.Fatal(err)
}
err = registry.RegisterCodec("myformat", &codec{})
if err != ErrCodecFormatAlreadyRegistered {
t.Fatalf("expected ErrDecoderFormatAlreadyRegistered, got: %v", err)
}
})
}
func TestCodecRegistry_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewCodecRegistry("", ini.LoadOptions{})
decoder := &codec{
v: map[string]interface{}{
"key": "value",
},
}
err := registry.RegisterCodec("myformat", decoder)
if err != nil {
t.Fatal(err)
}
v := map[string]interface{}{}
err = registry.Decode("myformat", []byte("key: value"), v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(decoder.v, v) {
t.Fatalf("decoded value does not match the expected one\nactual: %+v\nexpected: %+v", v, decoder.v)
}
})
t.Run("DecoderNotFound", func(t *testing.T) {
registry := NewCodecRegistry("", ini.LoadOptions{})
v := map[string]interface{}{}
err := registry.Decode("myformat", nil, v)
if err != ErrCodecNotFound {
t.Fatalf("expected ErrDecoderNotFound, got: %v", err)
}
})
}
func TestEncoderRegistry_Decode(t *testing.T) {
t.Run("OK", func(t *testing.T) {
registry := NewCodecRegistry("", ini.LoadOptions{})
encoder := &codec{
b: []byte("key: value"),
}
err := registry.RegisterCodec("myformat", encoder)
if err != nil {
t.Fatal(err)
}
b, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
if err != nil {
t.Fatal(err)
}
if string(b) != "key: value" {
t.Fatalf("expected 'key: value', got: %#v", string(b))
}
})
t.Run("EncoderNotFound", func(t *testing.T) {
registry := NewCodecRegistry("", ini.LoadOptions{})
_, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
if err != ErrCodecNotFound {
t.Fatalf("expected ErrEncoderNotFound, got: %v", err)
}
})
}

View file

@ -2,15 +2,20 @@ package toml
import (
"github.com/pelletier/go-toml/v2"
"github.com/spf13/viper/internal/encoding/codec"
)
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
// Codec implements the encoding.Codec interface for TOML encoding.
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func New(_ ...interface{}) codec.Codec {
return &Codec{}
}
func (*Codec) Encode(v map[string]interface{}) ([]byte, error) {
return toml.Marshal(v)
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (*Codec) Decode(b []byte, v map[string]interface{}) error {
return toml.Unmarshal(b, &v)
}

View file

@ -1,14 +1,21 @@
package yaml
import "gopkg.in/yaml.v3"
import (
"github.com/spf13/viper/internal/encoding/codec"
"gopkg.in/yaml.v3"
)
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
// Codec implements the encoding.Codec interface for YAML encoding.
type Codec struct{}
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
func New(_ ...interface{}) codec.Codec {
return &Codec{}
}
func (*Codec) Encode(v map[string]interface{}) ([]byte, error) {
return yaml.Marshal(v)
}
func (Codec) Decode(b []byte, v map[string]interface{}) error {
func (*Codec) Decode(b []byte, v map[string]interface{}) error {
return yaml.Unmarshal(b, &v)
}

119
viper.go
View file

@ -40,13 +40,7 @@ import (
"github.com/spf13/pflag"
"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"
)
// ConfigMarshalError happens when failing to marshal the configuration.
@ -219,9 +213,7 @@ type Viper struct {
logger Logger
// TODO: should probably be protected with a mutex
encoderRegistry *encoding.EncoderRegistry
decoderRegistry *encoding.DecoderRegistry
codecRegistry *encoding.CodecRegistry
}
// New returns an initialized Viper instance.
@ -242,11 +234,34 @@ func New() *Viper {
v.typeByDefValue = false
v.logger = jwwLogger{}
v.resetEncoding()
v.resetEncodingWithLazyInitializationMode()
return v
}
// NewWithLazyMode is the lazy initialization mode for benchmark
// todo: remove this API later when creating a PR
func NewWithLazyMode() *Viper {
v := new(Viper)
v.keyDelim = "."
v.configName = "config"
v.configPermissions = os.FileMode(0o644)
v.fs = afero.NewOsFs()
v.config = make(map[string]interface{})
v.parents = []string{}
v.override = make(map[string]interface{})
v.defaults = make(map[string]interface{})
v.kvstore = make(map[string]interface{})
v.pflags = make(map[string]FlagValue)
v.env = make(map[string][]string)
v.aliases = make(map[string]string)
v.typeByDefValue = false
v.logger = jwwLogger{}
v.resetEncodingWithLazyInitializationMode()
return v
}
// Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney.
// If you're unfamiliar with this style,
// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and
@ -290,7 +305,7 @@ func NewWithOptions(opts ...Option) *Viper {
opt.apply(v)
}
v.resetEncoding()
v.resetEncodingWithLazyInitializationMode()
return v
}
@ -304,82 +319,8 @@ func Reset() {
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
func (v *Viper) resetEncodingWithLazyInitializationMode() {
v.codecRegistry = encoding.NewCodecRegistry(v.keyDelim, v.iniLoadOptions)
}
type defaultRemoteProvider struct {
@ -1754,7 +1695,7 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
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)
err := v.codecRegistry.Decode(format, buf.Bytes(), c)
if err != nil {
return ConfigParseError{err}
}
@ -1769,7 +1710,7 @@ func (v *Viper) marshalWriter(f afero.File, 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)
b, err := v.codecRegistry.Encode(configType, c)
if err != nil {
return ConfigMarshalError{err}
}