types.go (502 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package ucfg import ( "fmt" "math" "reflect" "strconv" "strings" "sync/atomic" "time" "github.com/elastic/go-ucfg/parse" ) type value interface { typ(opts *options) (typeInfo, error) cpy(c context) value Context() context SetContext(c context) meta() *Meta setMeta(m *Meta) Len(opts *options) (int, error) reflect(opts *options) (reflect.Value, error) reify(opts *options) (interface{}, error) toBool(opts *options) (bool, error) toString(opts *options) (string, error) toInt(opts *options) (int64, error) toUint(opts *options) (uint64, error) toFloat(opts *options) (float64, error) toConfig(opts *options) (*Config, error) canCache() bool } type typeInfo struct { name string gotype reflect.Type } type context struct { parent value field string } type cfgBool struct { cfgPrimitive b bool } type cfgInt struct { cfgPrimitive i int64 } type cfgUint struct { cfgPrimitive u uint64 } type cfgFloat struct { cfgPrimitive f float64 } type cfgString struct { cfgPrimitive s string } type cfgSub struct { c *Config } type cfgNil struct{ cfgPrimitive } type cfgPrimitive struct { ctx context metadata *Meta } type cfgDynamic struct { cfgPrimitive id cacheID dyn dynValue } type dynValue interface { getValue(p *cfgPrimitive, opts *options) (value, error) String() string } type refDynValue reference type spliceDynValue struct { e varEvaler } var spliceSeq int32 func (c *context) empty() bool { return c.parent == nil } func (c *context) getParent() *Config { if c.parent == nil { return nil } if cfg, ok := c.parent.(cfgSub); ok { return cfg.c } return nil } func (c *context) path(sep string) string { if c.field == "" { return "" } if c.parent != nil { p := c.parent.Context() if parent := p.path(sep); parent != "" { return fmt.Sprintf("%v%v%v", parent, sep, c.field) } } return c.field } func (c *context) pathOf(field, sep string) string { if p := c.path(sep); p != "" { return fmt.Sprintf("%v%v%v", p, sep, field) } return field } func newBool(ctx context, m *Meta, b bool) *cfgBool { return &cfgBool{cfgPrimitive{ctx, m}, b} } func newInt(ctx context, m *Meta, i int64) *cfgInt { return &cfgInt{cfgPrimitive{ctx, m}, i} } func newUint(ctx context, m *Meta, u uint64) *cfgUint { return &cfgUint{cfgPrimitive{ctx, m}, u} } func newFloat(ctx context, m *Meta, f float64) *cfgFloat { return &cfgFloat{cfgPrimitive{ctx, m}, f} } func newString(ctx context, m *Meta, s string) *cfgString { return &cfgString{cfgPrimitive{ctx, m}, s} } func newRef(ctx context, m *Meta, ref *reference) *cfgDynamic { return newDyn(ctx, m, (*refDynValue)(ref)) } func newSplice(ctx context, m *Meta, s varEvaler) *cfgDynamic { return newDyn(ctx, m, spliceDynValue{s}) } func newDyn(ctx context, m *Meta, val dynValue) *cfgDynamic { seq := atomic.AddInt32(&spliceSeq, 1) dyn := &cfgDynamic{cfgPrimitive: cfgPrimitive{ctx, m}, dyn: val} dyn.id = cacheID(fmt.Sprintf("%8X-%4X-%p", time.Now().Unix(), seq, dyn)) return dyn } func (p *cfgPrimitive) Context() context { return p.ctx } func (p *cfgPrimitive) SetContext(c context) { p.ctx = c } func (p *cfgPrimitive) meta() *Meta { return p.metadata } func (p *cfgPrimitive) setMeta(m *Meta) { p.metadata = m } func (cfgPrimitive) Len(*options) (int, error) { return 1, nil } func (cfgPrimitive) toBool(*options) (bool, error) { return false, ErrTypeMismatch } func (cfgPrimitive) toString(*options) (string, error) { return "", ErrTypeMismatch } func (cfgPrimitive) toInt(*options) (int64, error) { return 0, ErrTypeMismatch } func (cfgPrimitive) toUint(*options) (uint64, error) { return 0, ErrTypeMismatch } func (cfgPrimitive) toFloat(*options) (float64, error) { return 0, ErrTypeMismatch } func (cfgPrimitive) toConfig(*options) (*Config, error) { return nil, ErrTypeMismatch } func (cfgPrimitive) canCache() bool { return true } func (c *cfgNil) cpy(ctx context) value { return &cfgNil{cfgPrimitive{ctx, c.metadata}} } func (*cfgNil) Len(*options) (int, error) { return 0, nil } func (*cfgNil) toString(*options) (string, error) { return "null", nil } func (*cfgNil) toInt(*options) (int64, error) { return 0, ErrTypeMismatch } func (*cfgNil) toUint(*options) (uint64, error) { return 0, ErrTypeMismatch } func (*cfgNil) toFloat(*options) (float64, error) { return 0, ErrTypeMismatch } func (*cfgNil) reify(*options) (interface{}, error) { return nil, nil } func (*cfgNil) typ(*options) (typeInfo, error) { return typeInfo{"any", reflect.PtrTo(tConfig)}, nil } func (c *cfgNil) meta() *Meta { return c.metadata } func (c *cfgNil) setMeta(m *Meta) { c.metadata = m } func (c *cfgNil) reflect(opts *options) (reflect.Value, error) { cfg, _ := c.toConfig(opts) return reflect.ValueOf(cfg), nil } func (c *cfgNil) toConfig(*options) (*Config, error) { n := New() n.ctx = c.ctx return n, nil } func (c *cfgBool) cpy(ctx context) value { return newBool(ctx, c.meta(), c.b) } func (c *cfgBool) toBool(*options) (bool, error) { return c.b, nil } func (c *cfgBool) reflect(*options) (reflect.Value, error) { return reflect.ValueOf(c.b), nil } func (c *cfgBool) reify(*options) (interface{}, error) { return c.b, nil } func (c *cfgBool) toString(*options) (string, error) { return fmt.Sprintf("%t", c.b), nil } func (c *cfgBool) typ(*options) (typeInfo, error) { return typeInfo{"bool", tBool}, nil } func (c *cfgInt) cpy(ctx context) value { return newInt(ctx, c.meta(), c.i) } func (c *cfgInt) toInt(*options) (int64, error) { return c.i, nil } func (c *cfgInt) toFloat(*options) (float64, error) { return float64(c.i), nil } func (c *cfgInt) reflect(*options) (reflect.Value, error) { return reflect.ValueOf(c.i), nil } func (c *cfgInt) reify(*options) (interface{}, error) { return c.i, nil } func (c *cfgInt) toString(*options) (string, error) { return fmt.Sprintf("%d", c.i), nil } func (c *cfgInt) typ(*options) (typeInfo, error) { return typeInfo{"int", tInt64}, nil } func (c *cfgInt) toUint(*options) (uint64, error) { if c.i < 0 { return 0, ErrNegative } return uint64(c.i), nil } func (c *cfgUint) cpy(ctx context) value { return newUint(ctx, c.meta(), c.u) } func (c *cfgUint) reflect(*options) (reflect.Value, error) { return reflect.ValueOf(c.u), nil } func (c *cfgUint) reify(*options) (interface{}, error) { return c.u, nil } func (c *cfgUint) toString(*options) (string, error) { return fmt.Sprintf("%d", c.u), nil } func (c *cfgUint) typ(*options) (typeInfo, error) { return typeInfo{"uint", tUint64}, nil } func (c *cfgUint) toUint(*options) (uint64, error) { return c.u, nil } func (c *cfgUint) toFloat(*options) (float64, error) { return float64(c.u), nil } func (c *cfgUint) toInt(*options) (int64, error) { if c.u > math.MaxInt64 { return 0, ErrOverflow } return int64(c.u), nil } func (c *cfgFloat) cpy(ctx context) value { return newFloat(ctx, c.meta(), c.f) } func (c *cfgFloat) toFloat(*options) (float64, error) { return c.f, nil } func (c *cfgFloat) reflect(*options) (reflect.Value, error) { return reflect.ValueOf(c.f), nil } func (c *cfgFloat) reify(*options) (interface{}, error) { return c.f, nil } func (c *cfgFloat) toString(*options) (string, error) { return fmt.Sprintf("%v", c.f), nil } func (c *cfgFloat) typ(*options) (typeInfo, error) { return typeInfo{"float", tFloat64}, nil } func (c *cfgFloat) toUint(*options) (uint64, error) { if c.f < 0 { return 0, ErrNegative } if c.f > math.MaxUint64 { return 0, ErrOverflow } return uint64(c.f), nil } func (c *cfgFloat) toInt(*options) (int64, error) { if c.f < math.MinInt64 || math.MaxInt64 < c.f { return 0, ErrOverflow } return int64(c.f), nil } func (c *cfgString) cpy(ctx context) value { return newString(ctx, c.meta(), c.s) } func (c *cfgString) reflect(*options) (reflect.Value, error) { return reflect.ValueOf(c.s), nil } func (c *cfgString) reify(*options) (interface{}, error) { return c.s, nil } func (c *cfgString) typ(*options) (typeInfo, error) { return typeInfo{"string", tString}, nil } func (c *cfgString) toBool(*options) (bool, error) { return strconv.ParseBool(c.s) } func (c *cfgString) toString(*options) (string, error) { return c.s, nil } func (c *cfgString) toInt(*options) (int64, error) { return strconv.ParseInt(c.s, 0, 64) } func (c *cfgString) toUint(*options) (uint64, error) { return strconv.ParseUint(c.s, 0, 64) } func (c *cfgString) toFloat(*options) (float64, error) { return strconv.ParseFloat(c.s, 64) } func (c cfgSub) Context() context { return c.c.ctx } func (cfgSub) toBool(*options) (bool, error) { return false, ErrTypeMismatch } func (cfgSub) toString(*options) (string, error) { return "", ErrTypeMismatch } func (cfgSub) toInt(*options) (int64, error) { return 0, ErrTypeMismatch } func (cfgSub) toUint(*options) (uint64, error) { return 0, ErrTypeMismatch } func (cfgSub) toFloat(*options) (float64, error) { return 0, ErrTypeMismatch } func (c cfgSub) toConfig(*options) (*Config, error) { return c.c, nil } func (c cfgSub) canCache() bool { return false } func (c cfgSub) Len(*options) (int, error) { arr := c.c.fields.array() if arr != nil { return len(arr), nil } return 1, nil } func (c cfgSub) typ(*options) (typeInfo, error) { return typeInfo{"object", reflect.PtrTo(tConfig)}, nil } // func (cfgSub) typ() (typeInfo, error) { return typeInfo{"object", reflect.PtrTo(tConfig)}, nil } func (c cfgSub) reflect(*options) (reflect.Value, error) { return reflect.ValueOf(c.c), nil } func (c cfgSub) meta() *Meta { return c.c.metadata } func (c cfgSub) setMeta(m *Meta) { c.c.metadata = m } func (c cfgSub) cpy(ctx context) value { newC := cfgSub{ c: &Config{ctx: ctx, metadata: c.c.metadata}, } dict := c.c.fields.dict() arr := c.c.fields.array() fields := &fields{} for name, f := range dict { ctx := f.Context() v := f.cpy(context{field: ctx.field, parent: newC}) fields.set(name, v) } if arr != nil { fields.a = make([]value, len(arr)) for i, f := range arr { ctx := f.Context() v := f.cpy(context{field: ctx.field, parent: newC}) fields.setAt(i, newC, v) } } newC.c.fields = fields return newC } func (c cfgSub) SetContext(ctx context) { if c.c.ctx.empty() { c.c.ctx = ctx } else { c.c = &Config{ ctx: ctx, fields: c.c.fields, } } } func (c cfgSub) reify(opts *options) (interface{}, error) { parentFields := opts.activeFields defer func() { opts.activeFields = parentFields }() fields := c.c.fields.dict() arr := c.c.fields.array() switch { case len(fields) == 0 && len(arr) == 0 && arr != nil: // preserve empty arrays return []interface{}{}, nil case len(fields) == 0 && len(arr) == 0: return nil, nil case len(fields) > 0 && len(arr) == 0: m := make(map[string]interface{}) for k, v := range fields { opts.activeFields = newFieldSet(parentFields) var err error if m[k], err = v.reify(opts); err != nil { return nil, err } } return m, nil case len(fields) == 0 && len(arr) > 0: m := make([]interface{}, len(arr)) for i, v := range arr { opts.activeFields = newFieldSet(parentFields) var err error if m[i], err = v.reify(opts); err != nil { return nil, err } } return m, nil default: m := make(map[string]interface{}) for k, v := range fields { opts.activeFields = newFieldSet(parentFields) var err error if m[k], err = v.reify(opts); err != nil { return nil, err } } for i, v := range arr { opts.activeFields = newFieldSet(parentFields) var err error m[fmt.Sprintf("%d", i)], err = v.reify(opts) if err != nil { return nil, err } } return m, nil } } func (d *cfgDynamic) typ(opts *options) (ti typeInfo, err error) { d.withValue(&err, opts, func(v value) { ti, err = v.typ(opts) }) return } func (d *cfgDynamic) cpy(c context) value { return newDyn(c, d.meta(), d.dyn) } func (d *cfgDynamic) Len(opts *options) (l int, err error) { d.withValue(&err, opts, func(v value) { l, err = v.Len(opts) }) return } func (d *cfgDynamic) reflect(opts *options) (rv reflect.Value, err error) { d.withValue(&err, opts, func(v value) { rv, err = v.reflect(opts) }) return } func (d *cfgDynamic) reify(opts *options) (rv interface{}, err error) { d.withValue(&err, opts, func(v value) { rv, err = v.reify(opts) }) return } func (d *cfgDynamic) toBool(opts *options) (b bool, err error) { d.withValue(&err, opts, func(v value) { b, err = v.toBool(opts) }) return } func (d *cfgDynamic) toString(opts *options) (s string, err error) { d.withValue(&err, opts, func(v value) { s, err = v.toString(opts) }) return } func (d *cfgDynamic) toInt(opts *options) (i int64, err error) { d.withValue(&err, opts, func(v value) { i, err = v.toInt(opts) }) return } func (d *cfgDynamic) toUint(opts *options) (u uint64, err error) { d.withValue(&err, opts, func(v value) { u, err = v.toUint(opts) }) return } func (d *cfgDynamic) toFloat(opts *options) (f float64, err error) { d.withValue(&err, opts, func(v value) { f, err = v.toFloat(opts) }) return } func (d *cfgDynamic) toConfig(opts *options) (cfg *Config, err error) { d.withValue(&err, opts, func(v value) { cfg, err = v.toConfig(opts) }) return } func (d *cfgDynamic) withValue(err *error, opts *options, fn func(value)) { var v value if v, *err = d.getValue(opts); *err == nil { fn(v) } } func (d *cfgDynamic) getValue(opts *options) (value, error) { return opts.parsed.cachedValue(d.id, func() (value, error) { return d.dyn.getValue(&d.cfgPrimitive, opts) }) } func (d cfgDynamic) canCache() bool { return false } func (r *refDynValue) String() string { ref := (*reference)(r) return ref.String() } func (r *refDynValue) getValue( p *cfgPrimitive, opts *options, ) (value, error) { ref := (*reference)(r) v, err := ref.resolveRef(p.ctx.getParent(), opts) // If not found or we have a cyclic reference we try the environment resolvers if v != nil || criticalResolveError(err) { return v, err } previousErr := err str, parseCfg, err := ref.resolveEnv(p.ctx.getParent(), opts) if err != nil { // TODO(ph): Not everything is an Error, will do some cleanup in another PR. if v, ok := previousErr.(Error); ok { if v.Reason() == ErrCyclicReference { return nil, previousErr } } return nil, err } return parseValue(p, opts, str, parseCfg) } func (s spliceDynValue) getValue( p *cfgPrimitive, opts *options, ) (value, error) { splice := s.e str, err := splice.eval(p.ctx.getParent(), opts) if err != nil { return nil, err } return parseValue(p, opts, str, parse.DefaultConfig) } func (s spliceDynValue) String() string { return "<splice>" } func parseValue(p *cfgPrimitive, opts *options, str string, parseCfg parse.Config) (value, error) { if opts.noParse { return nil, raiseNoParse(p.ctx, p.meta()) } // only set IgnoreCommas if the default has been changed. if opts.ignoreCommas { parseCfg.IgnoreCommas = opts.ignoreCommas } ifc, err := parse.ValueWithConfig(str, parseCfg) if err != nil { return nil, err } if ifc == nil { if strings.TrimSpace(str) == "" { return newString(p.ctx, p.meta(), str), nil } return &cfgNil{cfgPrimitive{ctx: p.ctx, metadata: p.meta()}}, nil } switch v := ifc.(type) { case bool: return newBool(p.ctx, p.meta(), v), nil case int64: return newInt(p.ctx, p.meta(), v), nil case uint64: return newUint(p.ctx, p.meta(), v), nil case float64: return newFloat(p.ctx, p.meta(), v), nil case string: return newString(p.ctx, p.meta(), v), nil } sub, err := normalize(opts, ifc) if err != nil { return nil, err } sub.ctx = p.ctx sub.metadata = p.metadata return cfgSub{sub}, nil } func isNil(v value) bool { if v == nil { return true } _, tst := v.(*cfgNil) return tst } func isSub(v value) bool { if v == nil { return false } _, tst := v.(cfgSub) return tst }