forked from toolshed/abra
.gitea
cli
cmd
pkg
scripts
tests
vendor
coopcloud.tech
dario.cat
git.coopcloud.tech
github.com
AlecAivazis
Azure
BurntSushi
Microsoft
ProtonMail
aymanbagabas
beorn7
cenkalti
cespare
charmbracelet
cloudflare
containerd
containers
cpuguy83
cyphar
davecgh
decentral1se
distribution
docker
emirpasic
felixge
fvbommel
ghodss
go-git
gcfg
scanner
token
types
.gitignore
LICENSE
Makefile
README
doc.go
errors.go
read.go
set.go
go-billy
go-git
go-logfmt
go-logr
go-viper
gogo
golang
google
gorilla
grpc-ecosystem
hashicorp
inconshreveable
jbenet
kballard
kevinburke
klauspost
lucasb-eyer
mattn
mgutz
miekg
mitchellh
moby
morikuni
muesli
munnerz
opencontainers
pjbgf
pkg
pmezard
prometheus
rivo
russross
schollz
sergi
sirupsen
skeema
spf13
stretchr
theupdateframework
urfave
xanzy
xeipuuv
go.opentelemetry.io
golang.org
google.golang.org
gopkg.in
gotest.tools
modules.txt
.dockerignore
.drone.yml
.envrc.sample
.gitignore
.goreleaser.yml
AUTHORS.md
Dockerfile
LICENSE
Makefile
README.md
go.mod
go.sum
renovate.json
335 lines
8.2 KiB
Go
335 lines
8.2 KiB
Go
package gcfg
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding"
|
|
"encoding/gob"
|
|
"fmt"
|
|
"math/big"
|
|
"reflect"
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
|
|
"gopkg.in/warnings.v0"
|
|
|
|
"github.com/go-git/gcfg/types"
|
|
)
|
|
|
|
type tag struct {
|
|
ident string
|
|
intMode string
|
|
}
|
|
|
|
func newTag(ts string) tag {
|
|
t := tag{}
|
|
s := strings.Split(ts, ",")
|
|
t.ident = s[0]
|
|
for _, tse := range s[1:] {
|
|
if strings.HasPrefix(tse, "int=") {
|
|
t.intMode = tse[len("int="):]
|
|
}
|
|
}
|
|
return t
|
|
}
|
|
|
|
func fieldFold(v reflect.Value, name string) (reflect.Value, tag) {
|
|
var n string
|
|
r0, _ := utf8.DecodeRuneInString(name)
|
|
if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) {
|
|
n = "X"
|
|
}
|
|
n += strings.Replace(name, "-", "_", -1)
|
|
f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool {
|
|
if !v.FieldByName(fieldName).CanSet() {
|
|
return false
|
|
}
|
|
f, _ := v.Type().FieldByName(fieldName)
|
|
t := newTag(f.Tag.Get("gcfg"))
|
|
if t.ident != "" {
|
|
return strings.EqualFold(t.ident, name)
|
|
}
|
|
return strings.EqualFold(n, fieldName)
|
|
})
|
|
if !ok {
|
|
return reflect.Value{}, tag{}
|
|
}
|
|
return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg"))
|
|
}
|
|
|
|
type setter func(destp interface{}, blank bool, val string, t tag) error
|
|
|
|
var errUnsupportedType = fmt.Errorf("unsupported type")
|
|
var errBlankUnsupported = fmt.Errorf("blank value not supported for type")
|
|
|
|
var setters = []setter{
|
|
typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
|
|
}
|
|
|
|
func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error {
|
|
dtu, ok := d.(encoding.TextUnmarshaler)
|
|
if !ok {
|
|
return errUnsupportedType
|
|
}
|
|
if blank {
|
|
return errBlankUnsupported
|
|
}
|
|
return dtu.UnmarshalText([]byte(val))
|
|
}
|
|
|
|
func boolSetter(d interface{}, blank bool, val string, t tag) error {
|
|
if blank {
|
|
reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true))
|
|
return nil
|
|
}
|
|
b, err := types.ParseBool(val)
|
|
if err == nil {
|
|
reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b))
|
|
}
|
|
return err
|
|
}
|
|
|
|
func intMode(mode string) types.IntMode {
|
|
var m types.IntMode
|
|
if strings.ContainsAny(mode, "dD") {
|
|
m |= types.Dec
|
|
}
|
|
if strings.ContainsAny(mode, "hH") {
|
|
m |= types.Hex
|
|
}
|
|
if strings.ContainsAny(mode, "oO") {
|
|
m |= types.Oct
|
|
}
|
|
return m
|
|
}
|
|
|
|
var typeModes = map[reflect.Type]types.IntMode{
|
|
reflect.TypeOf(int(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(int8(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(int16(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(int32(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(int64(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(uint(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(uint8(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
|
|
reflect.TypeOf(uint64(0)): types.Dec | types.Hex,
|
|
// use default mode (allow dec/hex/oct) for uintptr type
|
|
reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
|
|
}
|
|
|
|
func intModeDefault(t reflect.Type) types.IntMode {
|
|
m, ok := typeModes[t]
|
|
if !ok {
|
|
m = types.Dec | types.Hex | types.Oct
|
|
}
|
|
return m
|
|
}
|
|
|
|
func intSetter(d interface{}, blank bool, val string, t tag) error {
|
|
if blank {
|
|
return errBlankUnsupported
|
|
}
|
|
mode := intMode(t.intMode)
|
|
if mode == 0 {
|
|
mode = intModeDefault(reflect.TypeOf(d).Elem())
|
|
}
|
|
return types.ParseInt(d, val, mode)
|
|
}
|
|
|
|
func stringSetter(d interface{}, blank bool, val string, t tag) error {
|
|
if blank {
|
|
return errBlankUnsupported
|
|
}
|
|
dsp, ok := d.(*string)
|
|
if !ok {
|
|
return errUnsupportedType
|
|
}
|
|
*dsp = val
|
|
return nil
|
|
}
|
|
|
|
var kindSetters = map[reflect.Kind]setter{
|
|
reflect.String: stringSetter,
|
|
reflect.Bool: boolSetter,
|
|
reflect.Int: intSetter,
|
|
reflect.Int8: intSetter,
|
|
reflect.Int16: intSetter,
|
|
reflect.Int32: intSetter,
|
|
reflect.Int64: intSetter,
|
|
reflect.Uint: intSetter,
|
|
reflect.Uint8: intSetter,
|
|
reflect.Uint16: intSetter,
|
|
reflect.Uint32: intSetter,
|
|
reflect.Uint64: intSetter,
|
|
reflect.Uintptr: intSetter,
|
|
}
|
|
|
|
var typeSetters = map[reflect.Type]setter{
|
|
reflect.TypeOf(big.Int{}): intSetter,
|
|
}
|
|
|
|
func typeSetter(d interface{}, blank bool, val string, tt tag) error {
|
|
t := reflect.ValueOf(d).Type().Elem()
|
|
setter, ok := typeSetters[t]
|
|
if !ok {
|
|
return errUnsupportedType
|
|
}
|
|
return setter(d, blank, val, tt)
|
|
}
|
|
|
|
func kindSetter(d interface{}, blank bool, val string, tt tag) error {
|
|
k := reflect.ValueOf(d).Type().Elem().Kind()
|
|
setter, ok := kindSetters[k]
|
|
if !ok {
|
|
return errUnsupportedType
|
|
}
|
|
return setter(d, blank, val, tt)
|
|
}
|
|
|
|
func scanSetter(d interface{}, blank bool, val string, tt tag) error {
|
|
if blank {
|
|
return errBlankUnsupported
|
|
}
|
|
return types.ScanFully(d, val, 'v')
|
|
}
|
|
|
|
func newValue(c *warnings.Collector, sect string, vCfg reflect.Value,
|
|
vType reflect.Type) (reflect.Value, error) {
|
|
//
|
|
pv := reflect.New(vType)
|
|
dfltName := "default-" + sect
|
|
dfltField, _ := fieldFold(vCfg, dfltName)
|
|
var err error
|
|
if dfltField.IsValid() {
|
|
b := bytes.NewBuffer(nil)
|
|
ge := gob.NewEncoder(b)
|
|
if err = c.Collect(ge.EncodeValue(dfltField)); err != nil {
|
|
return pv, err
|
|
}
|
|
gd := gob.NewDecoder(bytes.NewReader(b.Bytes()))
|
|
if err = c.Collect(gd.DecodeValue(pv.Elem())); err != nil {
|
|
return pv, err
|
|
}
|
|
}
|
|
return pv, nil
|
|
}
|
|
|
|
func set(c *warnings.Collector, cfg interface{}, sect, sub, name string,
|
|
value string, blankValue bool, subsectPass bool) error {
|
|
//
|
|
vPCfg := reflect.ValueOf(cfg)
|
|
if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct {
|
|
panic(fmt.Errorf("config must be a pointer to a struct"))
|
|
}
|
|
vCfg := vPCfg.Elem()
|
|
vSect, _ := fieldFold(vCfg, sect)
|
|
if !vSect.IsValid() {
|
|
err := extraData{section: sect}
|
|
return c.Collect(err)
|
|
}
|
|
isSubsect := vSect.Kind() == reflect.Map
|
|
if subsectPass != isSubsect {
|
|
return nil
|
|
}
|
|
if isSubsect {
|
|
vst := vSect.Type()
|
|
if vst.Key().Kind() != reflect.String ||
|
|
vst.Elem().Kind() != reflect.Ptr ||
|
|
vst.Elem().Elem().Kind() != reflect.Struct {
|
|
panic(fmt.Errorf("map field for section must have string keys and "+
|
|
" pointer-to-struct values: section %q", sect))
|
|
}
|
|
if vSect.IsNil() {
|
|
vSect.Set(reflect.MakeMap(vst))
|
|
}
|
|
k := reflect.ValueOf(sub)
|
|
pv := vSect.MapIndex(k)
|
|
if !pv.IsValid() {
|
|
vType := vSect.Type().Elem().Elem()
|
|
var err error
|
|
if pv, err = newValue(c, sect, vCfg, vType); err != nil {
|
|
return err
|
|
}
|
|
vSect.SetMapIndex(k, pv)
|
|
}
|
|
vSect = pv.Elem()
|
|
} else if vSect.Kind() != reflect.Struct {
|
|
panic(fmt.Errorf("field for section must be a map or a struct: "+
|
|
"section %q", sect))
|
|
} else if sub != "" {
|
|
err := extraData{section: sect, subsection: &sub}
|
|
return c.Collect(err)
|
|
}
|
|
// Empty name is a special value, meaning that only the
|
|
// section/subsection object is to be created, with no values set.
|
|
if name == "" {
|
|
return nil
|
|
}
|
|
vVar, t := fieldFold(vSect, name)
|
|
if !vVar.IsValid() {
|
|
var err error
|
|
if isSubsect {
|
|
err = extraData{section: sect, subsection: &sub, variable: &name}
|
|
} else {
|
|
err = extraData{section: sect, variable: &name}
|
|
}
|
|
return c.Collect(err)
|
|
}
|
|
// vVal is either single-valued var, or newly allocated value within multi-valued var
|
|
var vVal reflect.Value
|
|
// multi-value if unnamed slice type
|
|
isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice ||
|
|
vVar.Type().Name() == "" && vVar.Kind() == reflect.Ptr && vVar.Type().Elem().Name() == "" && vVar.Type().Elem().Kind() == reflect.Slice
|
|
if isMulti && vVar.Kind() == reflect.Ptr {
|
|
if vVar.IsNil() {
|
|
vVar.Set(reflect.New(vVar.Type().Elem()))
|
|
}
|
|
vVar = vVar.Elem()
|
|
}
|
|
if isMulti && blankValue {
|
|
vVar.Set(reflect.Zero(vVar.Type()))
|
|
return nil
|
|
}
|
|
if isMulti {
|
|
vVal = reflect.New(vVar.Type().Elem()).Elem()
|
|
} else {
|
|
vVal = vVar
|
|
}
|
|
isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr
|
|
isNew := isDeref && vVal.IsNil()
|
|
// vAddr is address of value to set (dereferenced & allocated as needed)
|
|
var vAddr reflect.Value
|
|
switch {
|
|
case isNew:
|
|
vAddr = reflect.New(vVal.Type().Elem())
|
|
case isDeref && !isNew:
|
|
vAddr = vVal
|
|
default:
|
|
vAddr = vVal.Addr()
|
|
}
|
|
vAddrI := vAddr.Interface()
|
|
err, ok := error(nil), false
|
|
for _, s := range setters {
|
|
err = s(vAddrI, blankValue, value, t)
|
|
if err == nil {
|
|
ok = true
|
|
break
|
|
}
|
|
if err != errUnsupportedType {
|
|
return err
|
|
}
|
|
}
|
|
if !ok {
|
|
// in case all setters returned errUnsupportedType
|
|
return err
|
|
}
|
|
if isNew { // set reference if it was dereferenced and newly allocated
|
|
vVal.Set(vAddr)
|
|
}
|
|
if isMulti { // append if multi-valued
|
|
vVar.Set(reflect.Append(vVar, vVal))
|
|
}
|
|
return nil
|
|
}
|