116 lines
3.2 KiB
Go
116 lines
3.2 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// StoreValue parses value as the type of key, checks conf accepts it, sets it
|
|
// in conf and Viper and writes it to the config file Viper is using. Only the
|
|
// file's own settings are written back, not defaults or environment
|
|
// variables. It returns the stored value (as ValueOf gives it) and its kind.
|
|
func StoreValue(
|
|
conf *JobSearchConfig, key string, value string,
|
|
) (any, reflect.Kind, error) {
|
|
return storeValue(conf, key, value)
|
|
}
|
|
|
|
// storeValue is StoreValue for any section, so key types that
|
|
// JobSearchConfig doesn't have yet can be tested.
|
|
func storeValue(s Section, key string, value string) (any, reflect.Kind, error) {
|
|
confType, err := s.TypeOf(key)
|
|
if err != nil {
|
|
return nil, confType, err
|
|
}
|
|
|
|
// Check the value is of the right kind first, for a clearer error than
|
|
// SetValue gives.
|
|
if _, err := parseValue(confType, value); err != nil {
|
|
return nil, confType, fmt.Errorf(
|
|
"config value type not accepted, expected <%s>", confType,
|
|
)
|
|
}
|
|
|
|
// SetValue parses the string into the field's own type (e.g. int8,
|
|
// float32 or time.Duration) and rejects values the configuration itself
|
|
// would not accept, e.g. an unknown log level or an unparseable duration.
|
|
if err := s.SetValue(key, value); err != nil {
|
|
return nil, confType, err
|
|
}
|
|
confValue, err := s.ValueOf(key)
|
|
if err != nil {
|
|
return nil, confType, err
|
|
}
|
|
|
|
viper.Set(key, confValue)
|
|
file, err := readUsedConfigFile()
|
|
if err != nil {
|
|
return nil, confType, err
|
|
}
|
|
file.Set(key, confValue)
|
|
if err := file.WriteConfig(); err != nil {
|
|
return nil, confType, err
|
|
}
|
|
|
|
return confValue, confType, nil
|
|
}
|
|
|
|
// parseValue parses value as a value of kind, returning
|
|
// errors.ErrUnsupported for a kind it can't parse.
|
|
func parseValue(kind reflect.Kind, value string) (any, error) {
|
|
switch kind {
|
|
case reflect.Bool:
|
|
return strconv.ParseBool(value)
|
|
case reflect.String:
|
|
return value, nil
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
return strconv.ParseInt(value, 10, 64)
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
return strconv.ParseUint(value, 10, 64)
|
|
case reflect.Float32, reflect.Float64:
|
|
return strconv.ParseFloat(value, 64)
|
|
}
|
|
return nil, errors.ErrUnsupported
|
|
}
|
|
|
|
// PurgeValue rewrites the config file Viper is using without key. The
|
|
// file's other settings are kept as they are; defaults and environment
|
|
// variables are not written to it.
|
|
func PurgeValue(conf *JobSearchConfig, key string) error {
|
|
if _, err := conf.TypeOf(key); err != nil {
|
|
return err
|
|
}
|
|
|
|
file, err := readUsedConfigFile()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out := viper.New()
|
|
out.SetConfigFile(file.ConfigFileUsed())
|
|
for _, k := range file.AllKeys() {
|
|
if k != key {
|
|
out.Set(k, file.Get(k))
|
|
}
|
|
}
|
|
return out.WriteConfig()
|
|
}
|
|
|
|
// readUsedConfigFile reads the config file Viper is using into a new Viper, so
|
|
// it holds only the file's settings.
|
|
func readUsedConfigFile() (*viper.Viper, error) {
|
|
path := viper.ConfigFileUsed()
|
|
if path == "" {
|
|
return nil, errors.New("no configuration file in use")
|
|
}
|
|
file := viper.New()
|
|
file.SetConfigFile(path)
|
|
if err := file.ReadInConfig(); err != nil {
|
|
return nil, err
|
|
}
|
|
return file, nil
|
|
}
|