spf13--viper/internal/encoding/registry.go
2023-08-18 11:27:47 +08:00

115 lines
3 KiB
Go

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
}