1
0
mirror of https://github.com/khorevaa/logos.git synced 2025-02-07 13:43:50 +02:00
logos/internal/common/config.go
2021-02-01 17:50:15 +03:00

261 lines
6.1 KiB
Go

package common
import (
"crypto/md5"
"errors"
"io/ioutil"
"github.com/elastic/go-ucfg"
"github.com/elastic/go-ucfg/yaml"
)
// Config object to store hierarchical configurations into.
// See https://godoc.org/github.com/elastic/go-ucfg#Config
type Config ucfg.Config
// ConfigNamespace storing at most one configuration section by name and sub-section.
type ConfigNamespace struct {
name string
config *Config
}
var configOpts = []ucfg.Option{
ucfg.PathSep("."),
ucfg.ResolveEnv,
ucfg.VarExp,
ucfg.StructTag("logos-config"),
ucfg.ValidatorTag("logos-validate"),
ucfg.ReplaceValues,
}
func NewConfig() *Config {
return fromConfig(ucfg.New())
}
// NewConfigFrom creates a new Config object from the given input.
// From can be any kind of structured data (struct, map, array, slice).
//
// If from is a string, the contents is treated like raw YAML input. The string
// will be parsed and a structure config object is build from the parsed
// result.
func NewConfigFrom(from interface{}) (*Config, error) {
if str, ok := from.(string); ok {
c, err := yaml.NewConfig([]byte(str), configOpts...)
return fromConfig(c), err
}
c, err := ucfg.NewFrom(from, configOpts...)
return fromConfig(c), err
}
// MustNewConfigFrom creates a new Config object from the given input.
// From can be any kind of structured data (struct, map, array, slice).
//
// If from is a string, the contents is treated like raw YAML input. The string
// will be parsed and a structure config object is build from the parsed
// result.
//
// MustNewConfigFrom panics if an error occurs.
func MustNewConfigFrom(from interface{}) *Config {
cfg, err := NewConfigFrom(from)
if err != nil {
panic(err)
}
return cfg
}
func MergeConfigs(cfgs ...*Config) (*Config, error) {
config := NewConfig()
for _, c := range cfgs {
if err := config.Merge(c); err != nil {
return nil, err
}
}
return config, nil
}
func NewConfigWithYAML(in []byte, source string) (*Config, error) {
opts := append(
[]ucfg.Option{
ucfg.MetaData(ucfg.Meta{Source: source}),
},
configOpts...,
)
c, err := yaml.NewConfig(in, opts...)
return fromConfig(c), err
}
// OverwriteConfigOpts allow to change the globally set config option
func OverwriteConfigOpts(options []ucfg.Option) {
configOpts = options
}
func LoadFile(path string) (*Config, [md5.Size]byte, error) {
bs, err := ioutil.ReadFile(path)
if err != nil {
return nil, [md5.Size]byte{}, err
}
hash := md5.Sum(bs)
c, err := yaml.NewConfig(bs, configOpts...)
if err != nil {
return nil, hash, err
}
cfg := fromConfig(c)
return cfg, hash, err
}
func (c *Config) Merge(from interface{}) error {
return c.access().Merge(from, configOpts...)
}
func (c *Config) Unpack(to interface{}) error {
return c.access().Unpack(to, configOpts...)
}
func (c *Config) Path() string {
return c.access().Path(".")
}
func (c *Config) PathOf(field string) string {
return c.access().PathOf(field, ".")
}
func (c *Config) HasField(name string) bool {
return c.access().HasField(name)
}
func (c *Config) CountField(name string) (int, error) {
return c.access().CountField(name)
}
func (c *Config) Name() (string, error) {
return c.access().String("name", -1)
}
func (c *Config) MustName(fallback ...string) string {
name, err := c.Name()
if err != nil {
if len(fallback) > 0 {
return fallback[0]
}
return ""
}
return name
}
func (c *Config) Bool(name string, idx int) (bool, error) {
return c.access().Bool(name, idx, configOpts...)
}
func (c *Config) String(name string, idx int) (string, error) {
return c.access().String(name, idx, configOpts...)
}
func (c *Config) Int(name string, idx int) (int64, error) {
return c.access().Int(name, idx, configOpts...)
}
func (c *Config) Float(name string, idx int) (float64, error) {
return c.access().Float(name, idx, configOpts...)
}
func (c *Config) Child(name string, idx int) (*Config, error) {
sub, err := c.access().Child(name, idx, configOpts...)
return fromConfig(sub), err
}
func (c *Config) SetBool(name string, idx int, value bool) error {
return c.access().SetBool(name, idx, value, configOpts...)
}
func (c *Config) SetInt(name string, idx int, value int64) error {
return c.access().SetInt(name, idx, value, configOpts...)
}
func (c *Config) SetFloat(name string, idx int, value float64) error {
return c.access().SetFloat(name, idx, value, configOpts...)
}
func (c *Config) SetString(name string, idx int, value string) error {
return c.access().SetString(name, idx, value, configOpts...)
}
func (c *Config) SetChild(name string, idx int, value *Config) error {
return c.access().SetChild(name, idx, value.access(), configOpts...)
}
func (c *Config) IsDict() bool {
return c.access().IsDict()
}
func (c *Config) IsArray() bool {
return c.access().IsArray()
}
func fromConfig(in *ucfg.Config) *Config {
return (*Config)(in)
}
func (c *Config) access() *ucfg.Config {
return (*ucfg.Config)(c)
}
func (c *Config) GetFields() []string {
return c.access().GetFields()
}
// Unpack unpacks a configuration with at most one sub object. An sub object is
// ignored if it is disabled by setting `enabled: false`. If the configuration
// passed contains multiple active sub objects, Unpack will return an error.
func (ns *ConfigNamespace) Unpack(cfg *Config) error {
fields := cfg.GetFields()
if len(fields) == 0 {
return nil
}
var (
err error
found bool
)
for _, name := range fields {
var sub *Config
sub, err = cfg.Child(name, -1)
if err != nil {
// element is no configuration object -> continue so a namespace
// Config unpacked as a namespace can have other configuration
// values as well
continue
}
if ns.name != "" {
return errors.New("more than one namespace configured")
}
ns.name = name
ns.config = sub
found = true
}
if !found {
return err
}
return nil
}
// Name returns the configuration sections it's name if a section has been set.
func (ns *ConfigNamespace) Name() string {
return ns.name
}
// Config return the sub-configuration section if a section has been set.
func (ns *ConfigNamespace) Config() *Config {
return ns.config
}
// IsSet returns true if a sub-configuration section has been set.
func (ns *ConfigNamespace) IsSet() bool {
return ns.config != nil
}